proj-6.3.1/0000775000175000017500000000000013620226610007474 500000000000000proj-6.3.1/configure0000775000175000017500000243374713620226602011350 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for PROJ 6.3.1. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test -n \"\${ZSH_VERSION+set}\${BASH_VERSION+set}\" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO ECHO=\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO\$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test \"X\`printf %s \$ECHO\`\" = \"X\$ECHO\" \\ || test \"X\`print -r -- \$ECHO\`\" = \"X\$ECHO\" ) || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: https://github.com/OSGeo/PROJ/issues about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" SHELL=${CONFIG_SHELL-/bin/sh} test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='PROJ' PACKAGE_TARNAME='proj' PACKAGE_VERSION='6.3.1' PACKAGE_STRING='PROJ 6.3.1' PACKAGE_BUGREPORT='https://github.com/OSGeo/PROJ/issues' PACKAGE_URL='https://proj.org' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS USE_EXTERNAL_GTEST_FALSE USE_EXTERNAL_GTEST_TRUE GTEST_LIBS GTEST_CFLAGS SQLITE3_CHECK SQLITE3_LIBS SQLITE3_CFLAGS PKG_CONFIG THREAD_LIB MUTEX_SETTING JNI_INCLUDE NO_ZERO_AS_NULL_POINTER_CONSTANT_FLAG CXX_WFLAGS C_WFLAGS FLTO_FLAG CXXCPP CPP LT_SYS_LIBRARY_PATH OTOOL64 OTOOL LIPO NMEDIT DSYMUTIL MANIFEST_TOOL RANLIB ac_ct_AR AR DLLTOOL OBJDUMP NM ac_ct_DUMPBIN DUMPBIN LD FGREP EGREP GREP SED host_os host_vendor host_cpu host build_os build_vendor build_cpu build LIBTOOL LN_S HAVE_CXX11 am__fastdepCXX_FALSE am__fastdepCXX_TRUE CXXDEPMODE ac_ct_CXX CXXFLAGS CXX am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_shared enable_static with_pic enable_fast_install with_aix_soname with_gnu_ld with_sysroot enable_libtool_lock enable_lto with_jni with_mutex with_external_gtest ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CXX CXXFLAGS CCC LT_SYS_LIBRARY_PATH CPP CXXCPP PKG_CONFIG SQLITE3_CFLAGS SQLITE3_LIBS GTEST_CFLAGS GTEST_LIBS' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures PROJ 6.3.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/proj] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names System types: --build=BUILD configure for building on BUILD [guessed] --host=HOST cross-compile to build programs to run on HOST [BUILD] _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of PROJ 6.3.1:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-shared[=PKGS] build shared libraries [default=yes] --enable-static[=PKGS] build static libraries [default=yes] --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) --enable-lto enable LTO(link time optimization) (disabled by default) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use both] --with-aix-soname=aix|svr4|both shared library versioning (aka "SONAME") variant to provide on AIX, [default=aix]. --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-sysroot[=DIR] Search for dependent libraries within DIR (or the compiler's sysroot if not specified). --with-jni=dir Include Java/JNI support, add optional include dir --without-mutex Disable real mutex locks (lacking pthreads) --with-external-gtest Whether to use external Google Test Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags LT_SYS_LIBRARY_PATH User-defined run-time library search path. CPP C preprocessor CXXCPP C++ preprocessor PKG_CONFIG path to pkg-config utility SQLITE3_CFLAGS C compiler flags for SQLITE3, overriding pkg-config SQLITE3_LIBS linker flags for SQLITE3, overriding pkg-config GTEST_CFLAGS C compiler flags for GTEST, overriding pkg-config GTEST_LIBS linker flags for GTEST, overriding pkg-config Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . PROJ home page: . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF PROJ configure 6.3.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_cxx_try_cpp LINENO # ------------------------ # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp # ac_fn_cxx_try_link LINENO # ------------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_link # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## --------------------------------------------------- ## ## Report this to https://github.com/OSGeo/PROJ/issues ## ## --------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by PROJ $as_me 6.3.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in . "$srcdir"/.; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in . \"$srcdir\"/." "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. am__api_version='1.16' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='proj' VERSION='6.3.1' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi ac_config_headers="$ac_config_headers src/proj_config.h" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 $as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CXX" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CXX_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CXX_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CXX_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CXX_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 $as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then am__fastdepCXX_TRUE= am__fastdepCXX_FALSE='#' else am__fastdepCXX_TRUE='#' am__fastdepCXX_FALSE= fi ax_cxx_compile_cxx11_required=true ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 $as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } if ${ax_cv_cxx_compile_cxx11+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // https://stackoverflow.com/q/13728184 // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_cxx_compile_cxx11=yes else ax_cv_cxx_compile_cxx11=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 $as_echo "$ax_cv_cxx_compile_cxx11" >&6; } if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi if test x$ac_success = xno; then for switch in -std=c++11 -std=c++0x +std=c++11 "-h std=c++11"; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // https://stackoverflow.com/q/13728184 // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then HAVE_CXX11=0 { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 $as_echo "$as_me: No compiler with C++11 support was found" >&6;} else HAVE_CXX11=1 $as_echo "#define HAVE_CXX11 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi case `pwd` in *\ * | *\ *) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 $as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; esac macro_version='2.4.6' macro_revision='2.4.6' ltmain=$ac_aux_dir/ltmain.sh # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } if ${ac_cv_build+:} false; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; *) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' set x $ac_cv_build shift build_cpu=$1 build_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: build_os=$* IFS=$ac_save_IFS case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } if ${ac_cv_host+:} false; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; *) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' set x $ac_cv_host shift host_cpu=$1 host_vendor=$2 shift; shift # Remember, the first character of IFS is used to create $*, # except with old shells: host_os=$* IFS=$ac_save_IFS case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac # Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\(["`$\\]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\(["`\\]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to print strings" >&5 $as_echo_n "checking how to print strings... " >&6; } # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "" } case $ECHO in printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5 $as_echo "printf" >&6; } ;; print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5 $as_echo "print -r" >&6; } ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: cat" >&5 $as_echo "cat" >&6; } ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_SED" || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 $as_echo_n "checking for fgrep... " >&6; } if ${ac_cv_path_FGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 then ac_cv_path_FGREP="$GREP -F" else if test -z "$FGREP"; then ac_path_FGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in fgrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_FGREP" || continue # Check for GNU ac_path_FGREP and select it if it is found. # Check for GNU $ac_path_FGREP case `"$ac_path_FGREP" --version 2>&1` in *GNU*) ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'FGREP' >> "conftest.nl" "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_FGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_FGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_FGREP"; then as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_FGREP=$FGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 $as_echo "$ac_cv_path_FGREP" >&6; } FGREP="$ac_cv_path_FGREP" test -z "$GREP" && GREP=grep # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 $as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } if ${lt_cv_path_NM+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 $as_echo "$lt_cv_path_NM" >&6; } if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else if test -n "$ac_tool_prefix"; then for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DUMPBIN"; then ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DUMPBIN=$ac_cv_prog_DUMPBIN if test -n "$DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 $as_echo "$DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$DUMPBIN" && break done fi if test -z "$DUMPBIN"; then ac_ct_DUMPBIN=$DUMPBIN for ac_prog in dumpbin "link -dump" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DUMPBIN"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN if test -n "$ac_ct_DUMPBIN"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 $as_echo "$ac_ct_DUMPBIN" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_DUMPBIN" && break done if test "x$ac_ct_DUMPBIN" = x; then DUMPBIN=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DUMPBIN=$ac_ct_DUMPBIN fi fi case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm { $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 $as_echo_n "checking the name lister ($NM) interface... " >&6; } if ${lt_cv_nm_interface+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&5) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&5 (eval echo "\"\$as_me:$LINENO: output\"" >&5) cat conftest.out >&5 if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 $as_echo "$lt_cv_nm_interface" >&6; } # find the maximum length of command line arguments { $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 $as_echo_n "checking the maximum length of command line arguments... " >&6; } if ${lt_cv_sys_max_cmd_len+:} false; then : $as_echo_n "(cached) " >&6 else i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac fi if test -n "$lt_cv_sys_max_cmd_len"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 $as_echo "$lt_cv_sys_max_cmd_len" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 $as_echo "none" >&6; } fi max_cmd_len=$lt_cv_sys_max_cmd_len : ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5 $as_echo_n "checking how to convert $build file names to $host format... " >&6; } if ${lt_cv_to_host_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac fi to_host_file_cmd=$lt_cv_to_host_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_host_file_cmd" >&5 $as_echo "$lt_cv_to_host_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5 $as_echo_n "checking how to convert $build file names to toolchain format... " >&6; } if ${lt_cv_to_tool_file_cmd+:} false; then : $as_echo_n "(cached) " >&6 else #assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac fi to_tool_file_cmd=$lt_cv_to_tool_file_cmd { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_to_tool_file_cmd" >&5 $as_echo "$lt_cv_to_tool_file_cmd" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 $as_echo_n "checking for $LD option to reload object files... " >&6; } if ${lt_cv_ld_reload_flag+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_reload_flag='-r' fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 $as_echo "$lt_cv_ld_reload_flag" >&6; } reload_flag=$lt_cv_ld_reload_flag case $reload_flag in "" | " "*) ;; *) reload_flag=" $reload_flag" ;; esac reload_cmds='$LD$reload_flag -o $output$reload_objs' case $host_os in cygwin* | mingw* | pw32* | cegcc*) if test yes != "$GCC"; then reload_cmds=false fi ;; darwin*) if test yes = "$GCC"; then reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs' else reload_cmds='$LD$reload_flag -o $output$reload_objs' fi ;; esac if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. set dummy ${ac_tool_prefix}objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OBJDUMP"; then ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OBJDUMP=$ac_cv_prog_OBJDUMP if test -n "$OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 $as_echo "$OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OBJDUMP"; then ac_ct_OBJDUMP=$OBJDUMP # Extract the first word of "objdump", so it can be a program name with args. set dummy objdump; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OBJDUMP"; then ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OBJDUMP="objdump" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP if test -n "$ac_ct_OBJDUMP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 $as_echo "$ac_ct_OBJDUMP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OBJDUMP" = x; then OBJDUMP="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OBJDUMP=$ac_ct_OBJDUMP fi else OBJDUMP="$ac_cv_prog_OBJDUMP" fi test -z "$OBJDUMP" && OBJDUMP=objdump { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 $as_echo_n "checking how to recognize dependent libraries... " >&6; } if ${lt_cv_deplibs_check_method+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[4-9]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[45]*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]' lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9]\.[0-9]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[3-9]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 $as_echo "$lt_cv_deplibs_check_method" >&6; } file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[\1]\/[\1]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args. set dummy ${ac_tool_prefix}dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DLLTOOL"; then ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DLLTOOL=$ac_cv_prog_DLLTOOL if test -n "$DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DLLTOOL" >&5 $as_echo "$DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DLLTOOL"; then ac_ct_DLLTOOL=$DLLTOOL # Extract the first word of "dlltool", so it can be a program name with args. set dummy dlltool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DLLTOOL"; then ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DLLTOOL="dlltool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL if test -n "$ac_ct_DLLTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DLLTOOL" >&5 $as_echo "$ac_ct_DLLTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DLLTOOL" = x; then DLLTOOL="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DLLTOOL=$ac_ct_DLLTOOL fi else DLLTOOL="$ac_cv_prog_DLLTOOL" fi test -z "$DLLTOOL" && DLLTOOL=dlltool { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5 $as_echo_n "checking how to associate runtime and link libraries... " >&6; } if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5 $as_echo "$lt_cv_sharedlib_from_linklib_cmd" >&6; } sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO if test -n "$ac_tool_prefix"; then for ac_prog in ar do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} : ${AR_FLAGS=cru} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5 $as_echo_n "checking for archiver @FILE support... " >&6; } if ${lt_cv_ar_at_file+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ar_at_file=no cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5 (eval $lt_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5 $as_echo "$lt_cv_ar_at_file" >&6; } if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi test -z "$STRIP" && STRIP=: if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi test -z "$RANLIB" && RANLIB=: # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Check for command to grab the raw symbol name followed by C symbol from nm. { $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 $as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } if ${lt_cv_sys_global_symbol_pipe+:} false; then : $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[BCDEGRST]' # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[ABCDEGRST]' fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' ;; osf*) symcode='[BCDEGQRST]' ;; solaris*) symcode='[BDRT]' ;; sco3.2v5*) symcode='[DT]' ;; sysv4.2uw2*) symcode='[DT]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[ABDT]' ;; sysv4) symcode='[DFNSTU]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[ABCDGIRSTW]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK '"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&5 fi else echo "cannot find nm_test_var in $nlist" >&5 fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&5 fi else echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done fi if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 $as_echo "failed" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 $as_echo "ok" >&6; } fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[@]FILE' >/dev/null; then nm_file_list_spec='@' fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5 $as_echo_n "checking for sysroot... " >&6; } # Check whether --with-sysroot was given. if test "${with_sysroot+set}" = set; then : withval=$with_sysroot; else with_sysroot=no fi lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5 $as_echo "$with_sysroot" >&6; } as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${lt_sysroot:-no}" >&5 $as_echo "${lt_sysroot:-no}" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5 $as_echo_n "checking for a working dd... " >&6; } if ${ac_cv_path_lt_DD+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} if test -z "$lt_DD"; then ac_path_lt_DD_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in dd; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_lt_DD" || continue if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi $ac_path_lt_DD_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_lt_DD"; then : fi else ac_cv_path_lt_DD=$lt_DD fi rm -f conftest.i conftest2.i conftest.out fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5 $as_echo "$ac_cv_path_lt_DD" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5 $as_echo_n "checking how to truncate binary pipes... " >&6; } if ${lt_cv_truncate_bin+:} false; then : $as_echo_n "(cached) " >&6 else printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5 $as_echo "$lt_cv_truncate_bin" >&6; } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # Check whether --enable-libtool-lock was given. if test "${enable_libtool_lock+set}" = set; then : enableval=$enable_libtool_lock; fi test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '#line '$LINENO' "configure"' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 $as_echo_n "checking whether the C compiler needs -belf... " >&6; } if ${lt_cv_cc_needs_belf+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_cc_needs_belf=yes else lt_cv_cc_needs_belf=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 $as_echo "$lt_cv_cc_needs_belf" >&6; } if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args. set dummy ${ac_tool_prefix}mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$MANIFEST_TOOL"; then ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL if test -n "$MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MANIFEST_TOOL" >&5 $as_echo "$MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_MANIFEST_TOOL"; then ac_ct_MANIFEST_TOOL=$MANIFEST_TOOL # Extract the first word of "mt", so it can be a program name with args. set dummy mt; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_MANIFEST_TOOL"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_MANIFEST_TOOL="mt" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL if test -n "$ac_ct_MANIFEST_TOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_MANIFEST_TOOL" >&5 $as_echo "$ac_ct_MANIFEST_TOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_MANIFEST_TOOL" = x; then MANIFEST_TOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac MANIFEST_TOOL=$ac_ct_MANIFEST_TOOL fi else MANIFEST_TOOL="$ac_cv_prog_MANIFEST_TOOL" fi test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5 $as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; } if ${lt_cv_path_mainfest_tool+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5 $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&5 if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5 $as_echo "$lt_cv_path_mainfest_tool" >&6; } if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi case $host_os in rhapsody* | darwin*) if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$DSYMUTIL"; then ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi DSYMUTIL=$ac_cv_prog_DSYMUTIL if test -n "$DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 $as_echo "$DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_DSYMUTIL"; then ac_ct_DSYMUTIL=$DSYMUTIL # Extract the first word of "dsymutil", so it can be a program name with args. set dummy dsymutil; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_DSYMUTIL"; then ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL if test -n "$ac_ct_DSYMUTIL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 $as_echo "$ac_ct_DSYMUTIL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_DSYMUTIL" = x; then DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac DSYMUTIL=$ac_ct_DSYMUTIL fi else DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. set dummy ${ac_tool_prefix}nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$NMEDIT"; then ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi NMEDIT=$ac_cv_prog_NMEDIT if test -n "$NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 $as_echo "$NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_NMEDIT"; then ac_ct_NMEDIT=$NMEDIT # Extract the first word of "nmedit", so it can be a program name with args. set dummy nmedit; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_NMEDIT"; then ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_NMEDIT="nmedit" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT if test -n "$ac_ct_NMEDIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 $as_echo "$ac_ct_NMEDIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_NMEDIT" = x; then NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac NMEDIT=$ac_ct_NMEDIT fi else NMEDIT="$ac_cv_prog_NMEDIT" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. set dummy ${ac_tool_prefix}lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$LIPO"; then ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_LIPO="${ac_tool_prefix}lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi LIPO=$ac_cv_prog_LIPO if test -n "$LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 $as_echo "$LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_LIPO"; then ac_ct_LIPO=$LIPO # Extract the first word of "lipo", so it can be a program name with args. set dummy lipo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_LIPO+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LIPO"; then ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_LIPO="lipo" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO if test -n "$ac_ct_LIPO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 $as_echo "$ac_ct_LIPO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_LIPO" = x; then LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac LIPO=$ac_ct_LIPO fi else LIPO="$ac_cv_prog_LIPO" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. set dummy ${ac_tool_prefix}otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL"; then ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL="${ac_tool_prefix}otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL=$ac_cv_prog_OTOOL if test -n "$OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 $as_echo "$OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL"; then ac_ct_OTOOL=$OTOOL # Extract the first word of "otool", so it can be a program name with args. set dummy otool; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL"; then ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL="otool" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL if test -n "$ac_ct_OTOOL"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 $as_echo "$ac_ct_OTOOL" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL" = x; then OTOOL=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL=$ac_ct_OTOOL fi else OTOOL="$ac_cv_prog_OTOOL" fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. set dummy ${ac_tool_prefix}otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$OTOOL64"; then ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi OTOOL64=$ac_cv_prog_OTOOL64 if test -n "$OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 $as_echo "$OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_OTOOL64"; then ac_ct_OTOOL64=$OTOOL64 # Extract the first word of "otool64", so it can be a program name with args. set dummy otool64; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_OTOOL64"; then ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_OTOOL64="otool64" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 if test -n "$ac_ct_OTOOL64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 $as_echo "$ac_ct_OTOOL64" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_OTOOL64" = x; then OTOOL64=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac OTOOL64=$ac_ct_OTOOL64 fi else OTOOL64="$ac_cv_prog_OTOOL64" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 $as_echo_n "checking for -single_module linker flag... " >&6; } if ${lt_cv_apple_cc_single_mod+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&5 $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&5 # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&5 fi rm -rf libconftest.dylib* rm -f conftest.* fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 $as_echo "$lt_cv_apple_cc_single_mod" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 $as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } if ${lt_cv_ld_exported_symbols_list+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_ld_exported_symbols_list=yes else lt_cv_ld_exported_symbols_list=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 $as_echo "$lt_cv_ld_exported_symbols_list" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5 $as_echo_n "checking for -force_load linker flag... " >&6; } if ${lt_cv_ld_force_load+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&5 $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&5 echo "$AR cru libconftest.a conftest.o" >&5 $AR cru libconftest.a conftest.o 2>&5 echo "$RANLIB libconftest.a" >&5 $RANLIB libconftest.a 2>&5 cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5 $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&5 elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&5 fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5 $as_echo "$lt_cv_ld_force_load" >&6; } case $host_os in rhapsody* | darwin1.[012]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[91]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[012][,.]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in dlfcn.h do : ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default " if test "x$ac_cv_header_dlfcn_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_DLFCN_H 1 _ACEOF fi done func_stripname_cnf () { case $2 in .*) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%\\\\$2\$%%"`;; *) func_stripname_result=`$ECHO "$3" | $SED "s%^$1%%; s%$2\$%%"`;; esac } # func_stripname_cnf # Set options enable_dlopen=no enable_win32_dll=no # Check whether --enable-shared was given. if test "${enable_shared+set}" = set; then : enableval=$enable_shared; p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac else enable_shared=yes fi # Check whether --enable-static was given. if test "${enable_static+set}" = set; then : enableval=$enable_static; p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac else enable_static=yes fi # Check whether --with-pic was given. if test "${with_pic+set}" = set; then : withval=$with_pic; lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac else pic_mode=default fi # Check whether --enable-fast-install was given. if test "${enable_fast_install+set}" = set; then : enableval=$enable_fast_install; p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac else enable_fast_install=yes fi shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[5-9]*,yes) { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5 $as_echo_n "checking which variant of shared library versioning to provide... " >&6; } # Check whether --with-aix-soname was given. if test "${with_aix_soname+set}" = set; then : withval=$with_aix_soname; case $withval in aix|svr4|both) ;; *) as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5 ;; esac lt_cv_with_aix_soname=$with_aix_soname else if ${lt_cv_with_aix_soname+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_with_aix_soname=aix fi with_aix_soname=$lt_cv_with_aix_soname fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5 $as_echo "$with_aix_soname" >&6; } if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' test -z "$LN_S" && LN_S="ln -s" if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 $as_echo_n "checking for objdir... " >&6; } if ${lt_cv_objdir+:} false; then : $as_echo_n "(cached) " >&6 else rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 $as_echo "$lt_cv_objdir" >&6; } objdir=$lt_cv_objdir cat >>confdefs.h <<_ACEOF #define LT_OBJDIR "$lt_cv_objdir/" _ACEOF case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o func_cc_basename $compiler cc_basename=$func_cc_basename_result # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 $as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/${ac_tool_prefix}file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 $as_echo_n "checking for file... " >&6; } if ${lt_cv_path_MAGIC_CMD+:} false; then : $as_echo_n "(cached) " >&6 else case $MAGIC_CMD in [\\/*] | ?:[\\/]*) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/file"; then lt_cv_path_MAGIC_CMD=$ac_dir/"file" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac fi MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 $as_echo "$MAGIC_CMD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi else MAGIC_CMD=: fi fi fi ;; esac # Use C for the default configuration in the libtool script lt_save_CC=$CC ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o objext=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... if test -n "$compiler"; then lt_prog_compiler_no_builtin_flag= if test yes = "$GCC"; then case $cc_basename in nvcc*) lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;; *) lt_prog_compiler_no_builtin_flag=' -fno-builtin' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 $as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_rtti_exceptions=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_rtti_exceptions=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 $as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" else : fi fi lt_prog_compiler_wl= lt_prog_compiler_pic= lt_prog_compiler_static= if test yes = "$GCC"; then lt_prog_compiler_wl='-Wl,' lt_prog_compiler_static='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' fi lt_prog_compiler_pic='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) lt_prog_compiler_pic='-fPIC' ;; esac ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. lt_prog_compiler_can_build_shared=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic=-Kconform_pic fi ;; *) lt_prog_compiler_pic='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 lt_prog_compiler_wl='-Xlinker ' if test -n "$lt_prog_compiler_pic"; then lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) lt_prog_compiler_wl='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static='-Bstatic' else lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) lt_prog_compiler_wl='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? lt_prog_compiler_static='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) lt_prog_compiler_wl='-Wl,' # PIC (with -KPIC) is the default. lt_prog_compiler_static='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; # Lahey Fortran 8.1. lf95*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='--shared' lt_prog_compiler_static='--static' ;; nagfor*) # NAG Fortran compiler lt_prog_compiler_wl='-Wl,-Wl,,' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; ccc*) lt_prog_compiler_wl='-Wl,' # All Alpha code is PIC. lt_prog_compiler_static='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-qpic' lt_prog_compiler_static='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='' ;; *Sun\ F* | *Sun*Fortran*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' lt_prog_compiler_wl='-Wl,' ;; *Intel*\ [CF]*Compiler*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fPIC' lt_prog_compiler_static='-static' ;; *Portland\ Group*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-fpic' lt_prog_compiler_static='-Bstatic' ;; esac ;; esac ;; newsos6) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic='-fPIC -shared' ;; osf3* | osf4* | osf5*) lt_prog_compiler_wl='-Wl,' # All OSF/1 code is PIC. lt_prog_compiler_static='-non_shared' ;; rdos*) lt_prog_compiler_static='-non_shared' ;; solaris*) lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) lt_prog_compiler_wl='-Qoption ld ';; *) lt_prog_compiler_wl='-Wl,';; esac ;; sunos4*) lt_prog_compiler_wl='-Qoption ld ' lt_prog_compiler_pic='-PIC' lt_prog_compiler_static='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic='-Kconform_pic' lt_prog_compiler_static='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_pic='-KPIC' lt_prog_compiler_static='-Bstatic' ;; unicos*) lt_prog_compiler_wl='-Wl,' lt_prog_compiler_can_build_shared=no ;; uts4*) lt_prog_compiler_pic='-pic' lt_prog_compiler_static='-Bstatic' ;; *) lt_prog_compiler_can_build_shared=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic= ;; *) lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic=$lt_prog_compiler_pic fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5 $as_echo "$lt_cv_prog_compiler_pic" >&6; } lt_prog_compiler_pic=$lt_cv_prog_compiler_pic # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } if ${lt_cv_prog_compiler_pic_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 $as_echo "$lt_cv_prog_compiler_pic_works" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works"; then case $lt_prog_compiler_pic in "" | " "*) ;; *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; esac else lt_prog_compiler_pic= lt_prog_compiler_can_build_shared=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works=yes fi else lt_cv_prog_compiler_static_works=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 $as_echo "$lt_cv_prog_compiler_static_works" >&6; } if test yes = "$lt_cv_prog_compiler_static_works"; then : else lt_prog_compiler_static= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 $as_echo "$lt_cv_prog_compiler_c_o" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } runpath_var= allow_undefined_flag= always_export_symbols=no archive_cmds= archive_expsym_cmds= compiler_needs_object=no enable_shared_with_static_runtimes=no export_dynamic_flag_spec= export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' hardcode_automatic=no hardcode_direct=no hardcode_direct_absolute=no hardcode_libdir_flag_spec= hardcode_libdir_separator= hardcode_minus_L=no hardcode_shlibpath_var=unsupported inherit_rpath=no link_all_deplibs=unknown module_cmds= module_expsym_cmds= old_archive_from_new_cmds= old_archive_from_expsyms_cmds= thread_safe_flag_spec= whole_archive_flag_spec= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list include_expsyms= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; esac ld_shlibs=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[2-9]*) ;; *\ \(GNU\ Binutils\)\ [3-9]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' export_dynamic_flag_spec='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[3-9]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec='-L$libdir' export_dynamic_flag_spec='$wl--export-all-symbols' allow_undefined_flag=unsupported always_export_symbols=no enable_shared_with_static_runtimes=yes export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs=no fi ;; haiku*) archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs=yes ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; interix[3-9]*) hardcode_direct=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 whole_archive_flag_spec= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) export_dynamic_flag_spec='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else ld_shlibs=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) ld_shlibs=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac ;; sunos4*) archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= hardcode_direct=yes hardcode_shlibpath_var=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else ld_shlibs=no fi ;; esac if test no = "$ld_shlibs"; then runpath_var= hardcode_libdir_flag_spec= export_dynamic_flag_spec= whole_archive_flag_spec= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) allow_undefined_flag=unsupported always_export_symbols=yes archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. hardcode_minus_L=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. hardcode_direct=unsupported fi ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds='' hardcode_direct=yes hardcode_direct_absolute=yes hardcode_libdir_separator=':' link_all_deplibs=yes file_list_spec='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct=no hardcode_direct_absolute=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L=yes hardcode_libdir_flag_spec='-L$libdir' hardcode_libdir_separator= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. always_export_symbols=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. allow_undefined_flag='-berok' # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag="-z nodefs" archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath_+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath_=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath_"; then lt_cv_aix_libpath_=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath_ fi hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag=' $wl-bernotok' allow_undefined_flag=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec='$convenience' fi archive_cmds_need_lc=yes archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds='' ;; m68k) archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes ;; esac ;; bsdi[45]*) export_dynamic_flag_spec=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported always_export_symbols=yes file_list_spec='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, )='true' enable_shared_with_static_runtimes=yes exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib old_postinstall_cmds='chmod 644 $oldlib' postlink_cmds='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper hardcode_libdir_flag_spec=' ' allow_undefined_flag=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. old_archive_from_new_cmds='true' # FIXME: Should let the user specify the lib program. old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' enable_shared_with_static_runtimes=yes ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc=no hardcode_direct=no hardcode_automatic=yes hardcode_shlibpath_var=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec='' fi link_all_deplibs=yes allow_undefined_flag=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" else ld_shlibs=no fi ;; dgux*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; hpux9*) if test yes = "$GCC"; then archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes export_dynamic_flag_spec='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5 $as_echo_n "checking if $CC understands -b... " >&6; } if ${lt_cv_prog_compiler__b+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler__b=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -b" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler__b=yes fi else lt_cv_prog_compiler__b=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5 $as_echo "$lt_cv_prog_compiler__b" >&6; } if test yes = "$lt_cv_prog_compiler__b"; then archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi ;; esac fi if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec='$wl+b $wl$libdir' hardcode_libdir_separator=: case $host_cpu in hppa*64*|ia64*) hardcode_direct=no hardcode_shlibpath_var=no ;; *) hardcode_direct=yes hardcode_direct_absolute=yes export_dynamic_flag_spec='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. hardcode_minus_L=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5 $as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; } if ${lt_cv_irix_exported_symbol+:} false; then : $as_echo_n "(cached) " >&6 else save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int foo (void) { return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : lt_cv_irix_exported_symbol=yes else lt_cv_irix_exported_symbol=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5 $as_echo "$lt_cv_irix_exported_symbol" >&6; } if test yes = "$lt_cv_irix_exported_symbol"; then archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi else archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: inherit_rpath=yes link_all_deplibs=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler ld_shlibs=yes archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi hardcode_libdir_flag_spec='-R$libdir' hardcode_direct=yes hardcode_shlibpath_var=no ;; newsos6) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: hardcode_shlibpath_var=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct=yes hardcode_shlibpath_var=no hardcode_direct_absolute=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' hardcode_libdir_flag_spec='$wl-rpath,$libdir' export_dynamic_flag_spec='$wl-E' else archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' hardcode_libdir_flag_spec='$wl-rpath,$libdir' fi else ld_shlibs=no fi ;; os2*) hardcode_libdir_flag_spec='-L$libdir' hardcode_minus_L=yes allow_undefined_flag=unsupported shrext_cmds=.dll archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes=yes ;; osf3*) if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi archive_cmds_need_lc='no' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' hardcode_libdir_separator=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then allow_undefined_flag=' $wl-expect_unresolved $wl\*' archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec='$wl-rpath $wl$libdir' else allow_undefined_flag=' -expect_unresolved \*' archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly hardcode_libdir_flag_spec='-rpath $libdir' fi archive_cmds_need_lc='no' hardcode_libdir_separator=: ;; solaris*) no_undefined_flag=' -z defs' if test yes = "$GCC"; then wlarc='$wl' archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi hardcode_libdir_flag_spec='-R$libdir' hardcode_shlibpath_var=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else whole_archive_flag_spec='-z allextract$convenience -z defaultextract' fi ;; esac link_all_deplibs=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi hardcode_libdir_flag_spec='-L$libdir' hardcode_direct=yes hardcode_minus_L=yes hardcode_shlibpath_var=no ;; sysv4) case $host_vendor in sni) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' reload_cmds='$CC -r -o $output$reload_objs' hardcode_direct=no ;; motorola) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_direct=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' hardcode_shlibpath_var=no ;; sysv4.3*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no export_dynamic_flag_spec='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_shlibpath_var=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes ld_shlibs=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag='$wl-z,text' archive_cmds_need_lc=no hardcode_shlibpath_var=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag='$wl-z,text' allow_undefined_flag='$wl-z,nodefs' archive_cmds_need_lc=no hardcode_shlibpath_var=no hardcode_libdir_flag_spec='$wl-R,$libdir' hardcode_libdir_separator=':' link_all_deplibs=yes export_dynamic_flag_spec='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' hardcode_libdir_flag_spec='-L$libdir' hardcode_shlibpath_var=no ;; *) ld_shlibs=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) export_dynamic_flag_spec='$wl-Blargedynsym' ;; esac fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 $as_echo "$ld_shlibs" >&6; } test no = "$ld_shlibs" && can_build_shared=no with_gnu_ld=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc" in x|xyes) # Assume -lc should be added archive_cmds_need_lc=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl pic_flag=$lt_prog_compiler_pic compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag allow_undefined_flag= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc=no else lt_cv_archive_cmds_need_lc=yes fi allow_undefined_flag=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5 $as_echo "$lt_cv_archive_cmds_need_lc" >&6; } archive_cmds_need_lc=$lt_cv_archive_cmds_need_lc ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[lt_foo]++; } if (lt_freq[lt_foo] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([A-Za-z]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api" ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action= if test -n "$hardcode_libdir_flag_spec" || test -n "$runpath_var" || test yes = "$hardcode_automatic"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" && test no != "$hardcode_minus_L"; then # Linking always hardcodes the temporary library directory. hardcode_action=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 $as_echo "$hardcode_action" >&6; } if test relink = "$hardcode_action" || test yes = "$inherit_rpath"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes fi ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" if test "x$ac_cv_func_shl_load" = xyes; then : lt_cv_dlopen=shl_load else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 $as_echo_n "checking for shl_load in -ldld... " >&6; } if ${ac_cv_lib_dld_shl_load+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char shl_load (); int main () { return shl_load (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_shl_load=yes else ac_cv_lib_dld_shl_load=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 $as_echo "$ac_cv_lib_dld_shl_load" >&6; } if test "x$ac_cv_lib_dld_shl_load" = xyes; then : lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld else ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" if test "x$ac_cv_func_dlopen" = xyes; then : lt_cv_dlopen=dlopen else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 $as_echo_n "checking for dlopen in -ldl... " >&6; } if ${ac_cv_lib_dl_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dl_dlopen=yes else ac_cv_lib_dl_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 $as_echo "$ac_cv_lib_dl_dlopen" >&6; } if test "x$ac_cv_lib_dl_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 $as_echo_n "checking for dlopen in -lsvld... " >&6; } if ${ac_cv_lib_svld_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsvld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_svld_dlopen=yes else ac_cv_lib_svld_dlopen=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 $as_echo "$ac_cv_lib_svld_dlopen" >&6; } if test "x$ac_cv_lib_svld_dlopen" = xyes; then : lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 $as_echo_n "checking for dld_link in -ldld... " >&6; } if ${ac_cv_lib_dld_dld_link+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ldld $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dld_link (); int main () { return dld_link (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_dld_dld_link=yes else ac_cv_lib_dld_dld_link=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 $as_echo "$ac_cv_lib_dld_dld_link" >&6; } if test "x$ac_cv_lib_dld_dld_link" = xyes; then : lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld fi fi fi fi fi fi ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 $as_echo_n "checking whether a program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac else : # compilation failed lt_cv_dlopen_self=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 $as_echo "$lt_cv_dlopen_self" >&6; } if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 $as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } if ${lt_cv_dlopen_self_static+:} false; then : $as_echo_n "(cached) " >&6 else if test yes = "$cross_compiling"; then : lt_cv_dlopen_self_static=cross else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF #line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; } _LT_EOF if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&5 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; esac else : # compilation failed lt_cv_dlopen_self_static=no fi fi rm -fr conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 $as_echo "$lt_cv_dlopen_self_static" >&6; } fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi striplib= old_striplib= { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 $as_echo_n "checking whether stripping libraries is possible... " >&6; } if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi ;; *) { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } ;; esac fi # Report what library types will actually be built { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 $as_echo_n "checking if libtool supports shared libraries... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 $as_echo "$can_build_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 $as_echo_n "checking whether to build shared libraries... " >&6; } test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[4-9]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 $as_echo "$enable_shared" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 $as_echo_n "checking whether to build static libraries... " >&6; } # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 $as_echo "$enable_static" >&6; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu CC=$lt_save_CC if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then if ${ac_cv_prog_CXXCPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded for CXXCPP in "$CXX -E" "/lib/cpp" do ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CXXCPP=$CXXCPP fi CXXCPP=$ac_cv_prog_CXXCPP else ac_cv_prog_CXXCPP=$CXXCPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 $as_echo "$CXXCPP" >&6; } ac_preproc_ok=false for ac_cxx_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_cxx_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu else _lt_caught_CXX_error=yes fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu archive_cmds_need_lc_CXX=no allow_undefined_flag_CXX= always_export_symbols_CXX=no archive_expsym_cmds_CXX= compiler_needs_object_CXX=no export_dynamic_flag_spec_CXX= hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no hardcode_libdir_flag_spec_CXX= hardcode_libdir_separator_CXX= hardcode_minus_L_CXX=no hardcode_shlibpath_var_CXX=unsupported hardcode_automatic_CXX=no inherit_rpath_CXX=no module_cmds_CXX= module_expsym_cmds_CXX= link_all_deplibs_CXX=unknown old_archive_cmds_CXX=$old_archive_cmds reload_flag_CXX=$reload_flag reload_cmds_CXX=$reload_cmds no_undefined_flag_CXX= whole_archive_flag_spec_CXX= enable_shared_with_static_runtimes_CXX=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o objext_CXX=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC # save warnings/boilerplate of simple test code ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC compiler_CXX=$CC func_cc_basename $compiler cc_basename=$func_cc_basename_result if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' else lt_prog_compiler_no_builtin_flag_CXX= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration # Check whether --with-gnu-ld was given. if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes else with_gnu_ld=no fi ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 $as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 $as_echo_n "checking for GNU ld... " >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi if ${lt_cv_path_LD+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &5 $as_echo "$LD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } if ${lt_cv_prog_gnu_ld+:} false; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 $as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else whole_archive_flag_spec_CXX= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } ld_shlibs_CXX=yes case $host_os in aix3*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aix[4-9]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. archive_cmds_CXX='' hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes file_list_spec_CXX='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. hardcode_direct_CXX=no hardcode_direct_absolute_CXX=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[012]|aix4.[012].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 hardcode_direct_CXX=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking hardcode_minus_L_CXX=yes hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_libdir_separator_CXX= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi export_dynamic_flag_spec_CXX='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. always_export_symbols_CXX=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. no_undefined_flag_CXX='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then hardcode_libdir_flag_spec_CXX='$wl-R $libdir:/usr/lib:/lib' allow_undefined_flag_CXX="-z nodefs" archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else if ${lt_cv_aix_libpath__CXX+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : lt_aix_libpath_sed=' /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }' lt_cv_aix_libpath__CXX=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test -z "$lt_cv_aix_libpath__CXX"; then lt_cv_aix_libpath__CXX=/usr/lib:/lib fi fi aix_libpath=$lt_cv_aix_libpath__CXX fi hardcode_libdir_flag_spec_CXX='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. no_undefined_flag_CXX=' $wl-bernotok' allow_undefined_flag_CXX=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives whole_archive_flag_spec_CXX='$convenience' fi archive_cmds_need_lc_CXX=yes archive_expsym_cmds_CXX='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi archive_expsym_cmds_CXX="$archive_expsym_cmds_CXX"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then allow_undefined_flag_CXX=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else ld_shlibs_CXX=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. hardcode_libdir_flag_spec_CXX=' ' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=yes file_list_spec_CXX='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. archive_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, CXX)='true' enable_shared_with_static_runtimes_CXX=yes # Don't use ranlib old_postinstall_cmds_CXX='chmod 644 $oldlib' postlink_cmds_CXX='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, # as there is no search path for DLLs. hardcode_libdir_flag_spec_CXX='-L$libdir' export_dynamic_flag_spec_CXX='$wl--export-all-symbols' allow_undefined_flag_CXX=unsupported always_export_symbols_CXX=no enable_shared_with_static_runtimes_CXX=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... archive_expsym_cmds_CXX='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else ld_shlibs_CXX=no fi ;; esac ;; darwin* | rhapsody*) archive_cmds_need_lc_CXX=no hardcode_direct_CXX=no hardcode_automatic_CXX=yes hardcode_shlibpath_var_CXX=unsupported if test yes = "$lt_cv_ld_force_load"; then whole_archive_flag_spec_CXX='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' else whole_archive_flag_spec_CXX='' fi link_all_deplibs_CXX=yes allow_undefined_flag_CXX=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" module_expsym_cmds_CXX="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" if test yes != "$lt_cv_apple_cc_single_mod"; then archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" archive_expsym_cmds_CXX="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi else ld_shlibs_CXX=no fi ;; os2*) hardcode_libdir_flag_spec_CXX='-L$libdir' hardcode_minus_L_CXX=yes allow_undefined_flag_CXX=unsupported shrext_cmds=.dll archive_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' archive_expsym_cmds_CXX='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' old_archive_From_new_cmds_CXX='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' enable_shared_with_static_runtimes_CXX=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF ld_shlibs_CXX=no ;; freebsd-elf*) archive_cmds_need_lc_CXX=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions ld_shlibs_CXX=yes ;; haiku*) archive_cmds_CXX='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' link_all_deplibs_CXX=yes ;; hpux9*) hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: export_dynamic_flag_spec_CXX='$wl-E' hardcode_direct_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then hardcode_libdir_flag_spec_CXX='$wl+b $wl$libdir' hardcode_libdir_separator_CXX=: case $host_cpu in hppa*64*|ia64*) ;; *) export_dynamic_flag_spec_CXX='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no ;; *) hardcode_direct_CXX=yes hardcode_direct_absolute_CXX=yes hardcode_minus_L_CXX=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; aCC*) case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) archive_cmds_CXX='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; interix[3-9]*) hardcode_direct_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' archive_expsym_cmds_CXX='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi link_all_deplibs_CXX=yes ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: inherit_rpath_CXX=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac archive_cmds_need_lc_CXX=no hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [1-5].* | *pgcpp\ [1-5].*) prelink_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' old_archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' archive_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl--rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' whole_archive_flag_spec_CXX='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH hardcode_libdir_flag_spec_CXX='-rpath $libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' export_dynamic_flag_spec_CXX='$wl--export-dynamic' archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' hardcode_libdir_flag_spec_CXX='-R$libdir' whole_archive_flag_spec_CXX='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' compiler_needs_object_CXX=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; m88k*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) ld_shlibs_CXX=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then hardcode_direct_CXX=yes hardcode_shlibpath_var_CXX=no hardcode_direct_absolute_CXX=yes archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' export_dynamic_flag_spec_CXX='$wl-E' whole_archive_flag_spec_CXX=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else ld_shlibs_CXX=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath,$libdir' hardcode_libdir_separator_CXX=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; cxx*) case $host in osf3*) allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' ;; *) allow_undefined_flag_CXX=' -expect_unresolved \*' archive_cmds_CXX='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' hardcode_libdir_flag_spec_CXX='-rpath $libdir' ;; esac hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then allow_undefined_flag_CXX=' $wl-expect_unresolved $wl\*' case $host in osf3*) archive_cmds_CXX='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac hardcode_libdir_flag_spec_CXX='$wl-rpath $wl$libdir' hardcode_libdir_separator_CXX=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support ld_shlibs_CXX=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ archive_cmds_need_lc_CXX=yes no_undefined_flag_CXX=' -zdefs' archive_cmds_CXX='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' hardcode_libdir_flag_spec_CXX='-R$libdir' hardcode_shlibpath_var_CXX=no case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' ;; esac link_all_deplibs_CXX=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then no_undefined_flag_CXX=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then archive_cmds_CXX='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. archive_cmds_CXX='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi hardcode_libdir_flag_spec_CXX='$wl-R $wl$libdir' case $host_os in solaris2.[0-5] | solaris2.[0-5].*) ;; *) whole_archive_flag_spec_CXX='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) no_undefined_flag_CXX='$wl-z,text' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. no_undefined_flag_CXX='$wl-z,text' allow_undefined_flag_CXX='$wl-z,nodefs' archive_cmds_need_lc_CXX=no hardcode_shlibpath_var_CXX=no hardcode_libdir_flag_spec_CXX='$wl-R,$libdir' hardcode_libdir_separator_CXX=':' link_all_deplibs_CXX=yes export_dynamic_flag_spec_CXX='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) archive_cmds_CXX='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' old_archive_cmds_CXX='$CC -Tprelink_objects $oldobjs~ '"$old_archive_cmds_CXX" reload_cmds_CXX='$CC -Tprelink_objects $reload_objs~ '"$reload_cmds_CXX" ;; *) archive_cmds_CXX='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' archive_expsym_cmds_CXX='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; *) # FIXME: insert proper C++ library support ld_shlibs_CXX=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no GCC_CXX=$GXX LD_CXX=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... # Dependencies to place before and after the object being linked: predep_objects_CXX= postdep_objects_CXX= predeps_CXX= postdeps_CXX= compiler_lib_search_path_CXX= cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$compiler_lib_search_path_CXX"; then compiler_lib_search_path_CXX=$prev$p else compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$postdeps_CXX"; then postdeps_CXX=$prev$p else postdeps_CXX="${postdeps_CXX} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$predep_objects_CXX"; then predep_objects_CXX=$p else predep_objects_CXX="$predep_objects_CXX $p" fi else if test -z "$postdep_objects_CXX"; then postdep_objects_CXX=$p else postdep_objects_CXX="$postdep_objects_CXX $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling CXX test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken case $host_os in interix[3-9]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. predep_objects_CXX= postdep_objects_CXX= postdeps_CXX= ;; esac case " $postdeps_CXX " in *" -lc "*) archive_cmds_need_lc_CXX=no ;; esac compiler_lib_search_dirs_CXX= if test -n "${compiler_lib_search_path_CXX}"; then compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi lt_prog_compiler_wl_CXX= lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX= # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' fi lt_prog_compiler_pic_CXX='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support lt_prog_compiler_pic_CXX='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries lt_prog_compiler_pic_CXX='-DDLL_EXPORT' case $host_os in os2*) lt_prog_compiler_static_CXX='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files lt_prog_compiler_pic_CXX='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all lt_prog_compiler_pic_CXX= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. lt_prog_compiler_static_CXX= ;; interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; *) lt_prog_compiler_pic_CXX='-fPIC' ;; esac else case $host_os in aix[4-9]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor lt_prog_compiler_static_CXX='-Bstatic' else lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; dgux*) case $cc_basename in ec++*) lt_prog_compiler_pic_CXX='-KPIC' ;; ghcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then lt_prog_compiler_pic_CXX='+Z' fi ;; aCC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) lt_prog_compiler_pic_CXX='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_static_CXX='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler lt_prog_compiler_wl_CXX='--backend -Wl,' lt_prog_compiler_pic_CXX='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fPIC' lt_prog_compiler_static_CXX='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-fpic' lt_prog_compiler_static_CXX='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; xlc* | xlC* | bgxl[cC]* | mpixl[cC]*) # IBM XL 8.0, 9.0 on PPC and BlueGene lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-qpic' lt_prog_compiler_static_CXX='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) lt_prog_compiler_pic_CXX='-W c,exportall' ;; *) ;; esac ;; netbsd*) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. lt_prog_compiler_pic_CXX='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) lt_prog_compiler_wl_CXX='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 lt_prog_compiler_pic_CXX='-pic' ;; cxx*) # Digital/Compaq C++ lt_prog_compiler_wl_CXX='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. lt_prog_compiler_pic_CXX= lt_prog_compiler_static_CXX='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' lt_prog_compiler_wl_CXX='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler lt_prog_compiler_pic_CXX='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x lt_prog_compiler_pic_CXX='-pic' lt_prog_compiler_static_CXX='-Bstatic' ;; lcc*) # Lucid lt_prog_compiler_pic_CXX='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) lt_prog_compiler_wl_CXX='-Wl,' lt_prog_compiler_pic_CXX='-KPIC' lt_prog_compiler_static_CXX='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 lt_prog_compiler_pic_CXX='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) lt_prog_compiler_can_build_shared_CXX=no ;; esac fi case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) lt_prog_compiler_pic_CXX= ;; *) lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 $as_echo_n "checking for $compiler option to produce PIC... " >&6; } if ${lt_cv_prog_compiler_pic_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_CXX=$lt_prog_compiler_pic_CXX fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_CXX" >&6; } lt_prog_compiler_pic_CXX=$lt_cv_prog_compiler_pic_CXX # # Check to make sure the PIC flag actually works. # if test -n "$lt_prog_compiler_pic_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 $as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } if ${lt_cv_prog_compiler_pic_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_pic_works_CXX=no ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_pic_works_CXX=yes fi fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_pic_works_CXX"; then case $lt_prog_compiler_pic_CXX in "" | " "*) ;; *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; esac else lt_prog_compiler_pic_CXX= lt_prog_compiler_can_build_shared_CXX=no fi fi # # Check to make sure the static flag actually works. # wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 $as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } if ${lt_cv_prog_compiler_static_works_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_static_works_CXX=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $lt_tmp_static_flag" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&5 $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then lt_cv_prog_compiler_static_works_CXX=yes fi else lt_cv_prog_compiler_static_works_CXX=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 $as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } if test yes = "$lt_cv_prog_compiler_static_works_CXX"; then : else lt_prog_compiler_static_CXX= fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 $as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } if ${lt_cv_prog_compiler_c_o_CXX+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_prog_compiler_c_o_CXX=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 echo "$as_me:$LINENO: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then lt_cv_prog_compiler_c_o_CXX=yes fi fi chmod u+w . 2>&5 $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 $as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } hard_links=nottested if test no = "$lt_cv_prog_compiler_c_o_CXX" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 $as_echo_n "checking if we can lock with hard links... " >&6; } hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 $as_echo "$hard_links" >&6; } if test no = "$hard_links"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5 $as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;} need_locks=warn fi else need_locks=no fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 $as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' case $host_os in aix[4-9]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else export_symbols_cmds_CXX='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) export_symbols_cmds_CXX=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) exclude_expsyms_CXX='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' exclude_expsyms_CXX='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname' ;; esac ;; *) export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 $as_echo "$ld_shlibs_CXX" >&6; } test no = "$ld_shlibs_CXX" && can_build_shared=no with_gnu_ld_CXX=$with_gnu_ld # # Do we need to explicitly link libc? # case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added archive_cmds_need_lc_CXX=yes if test yes,yes = "$GCC,$enable_shared"; then case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 $as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } if ${lt_cv_archive_cmds_need_lc_CXX+:} false; then : $as_echo_n "(cached) " >&6 else $RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$lt_prog_compiler_wl_CXX pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$allow_undefined_flag_CXX allow_undefined_flag_CXX= if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } then lt_cv_archive_cmds_need_lc_CXX=no else lt_cv_archive_cmds_need_lc_CXX=yes fi allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc_CXX" >&5 $as_echo "$lt_cv_archive_cmds_need_lc_CXX" >&6; } archive_cmds_need_lc_CXX=$lt_cv_archive_cmds_need_lc_CXX ;; esac fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 $as_echo_n "checking dynamic linker characteristics... " >&6; } library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[4-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a(lib.so.V)' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[45]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([a-zA-Z]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[23].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[01]* | freebsdelf3.[01]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[3-9]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. hardcode_libdir_flag_spec_CXX='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH if ${lt_cv_shlibpath_overrides_runpath+:} false; then : $as_echo_n "(cached) " >&6 else lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : lt_cv_shlibpath_overrides_runpath=yes fi fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LDFLAGS=$save_LDFLAGS libdir=$save_libdir fi shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 $as_echo "$dynamic_linker" >&6; } test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 $as_echo_n "checking how to hardcode library paths into programs... " >&6; } hardcode_action_CXX= if test -n "$hardcode_libdir_flag_spec_CXX" || test -n "$runpath_var_CXX" || test yes = "$hardcode_automatic_CXX"; then # We can hardcode non-existent directories. if test no != "$hardcode_direct_CXX" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" && test no != "$hardcode_minus_L_CXX"; then # Linking always hardcodes the temporary library directory. hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. hardcode_action_CXX=unsupported fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 $as_echo "$hardcode_action_CXX" >&6; } if test relink = "$hardcode_action_CXX" || test yes = "$inherit_rpath_CXX"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_commands="$ac_config_commands libtool" # Only expand once: { $as_echo "$as_me:${as_lineno-$LINENO}: checking C_WFLAGS for maximum warnings" >&5 $as_echo_n "checking C_WFLAGS for maximum warnings... " >&6; } if ${ac_cv_cflags_warn_all+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_cflags_warn_all="no, unknown" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_save_CFLAGS="$CFLAGS" for ac_arg in "-pedantic -Wdeclaration-after-statement % -Wall -Wdeclaration-after-statement" "-pedantic % -Wall" "-xstrconst % -v" "-std1 % -verbose -w0 -warnprotos" "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" "-ansi -ansiE % -fullwarn" "+ESlit % +w1" "-Xc % -pvctl,fullmsg" "-h conform % -h msglevel 2" # do CFLAGS="$ac_save_CFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_cflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done CFLAGS="$ac_save_CFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cflags_warn_all" >&5 $as_echo "$ac_cv_cflags_warn_all" >&6; } case ".$ac_cv_cflags_warn_all" in .ok|.ok,*) ;; .|.no|.no,*) ;; *) if echo " $C_WFLAGS " | grep " $ac_cv_cflags_warn_all " 2>&1 >/dev/null then { { $as_echo "$as_me:${as_lineno-$LINENO}: : C_WFLAGS does contain \$ac_cv_cflags_warn_all"; } >&5 (: C_WFLAGS does contain $ac_cv_cflags_warn_all) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } else { { $as_echo "$as_me:${as_lineno-$LINENO}: : C_WFLAGS=\"\$C_WFLAGS \$ac_cv_cflags_warn_all\""; } >&5 (: C_WFLAGS="$C_WFLAGS $ac_cv_cflags_warn_all") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } C_WFLAGS="$C_WFLAGS $ac_cv_cflags_warn_all" fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking CXX_WFLAGS for maximum warnings" >&5 $as_echo_n "checking CXX_WFLAGS for maximum warnings... " >&6; } if ${ac_cv_cxxflags_warn_all+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_cxxflags_warn_all="no, unknown" ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_save_CXXFLAGS="$CXXFLAGS" for ac_arg in "-pedantic % -Wall" "-xstrconst % -v" "-std1 % -verbose -w0 -warnprotos" "-qlanglvl=ansi % -qsrcmsg -qinfo=all:noppt:noppc:noobs:nocnd" "-ansi -ansiE % -fullwarn" "+ESlit % +w1" "-Xc % -pvctl,fullmsg" "-h conform % -h msglevel 2" # do CXXFLAGS="$ac_save_CXXFLAGS "`echo $ac_arg | sed -e 's,%%.*,,' -e 's,%,,'` cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { return 0; ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_cxxflags_warn_all=`echo $ac_arg | sed -e 's,.*% *,,'` ; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done CXXFLAGS="$ac_save_CXXFLAGS" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxxflags_warn_all" >&5 $as_echo "$ac_cv_cxxflags_warn_all" >&6; } case ".$ac_cv_cxxflags_warn_all" in .ok|.ok,*) ;; .|.no|.no,*) ;; *) if echo " $CXX_WFLAGS " | grep " $ac_cv_cxxflags_warn_all " 2>&1 >/dev/null then { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXX_WFLAGS does contain \$ac_cv_cxxflags_warn_all"; } >&5 (: CXX_WFLAGS does contain $ac_cv_cxxflags_warn_all) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } else { { $as_echo "$as_me:${as_lineno-$LINENO}: : CXX_WFLAGS=\"\$CXX_WFLAGS \$ac_cv_cxxflags_warn_all\""; } >&5 (: CXX_WFLAGS="$CXX_WFLAGS $ac_cv_cxxflags_warn_all") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } CXX_WFLAGS="$CXX_WFLAGS $ac_cv_cxxflags_warn_all" fi ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror -we10006" >&5 $as_echo_n "checking whether C compiler accepts -Werror -we10006... " >&6; } if ${ax_cv_check_cflags___Werror__we10006+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -we10006" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ax_cv_check_cflags___Werror__we10006=yes else ax_cv_check_cflags___Werror__we10006=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags___Werror__we10006" >&5 $as_echo "$ax_cv_check_cflags___Werror__we10006" >&6; } if test "x$ax_cv_check_cflags___Werror__we10006" = xyes; then : ERROR_ON_UNKNOWN_OPTIONS="-Werror -we10006" else ERROR_ON_UNKNOWN_OPTIONS="-Werror" fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-diag-disable 188,1684,2259,2304,3280,11074,11076" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -diag-disable 188,1684,2259,2304,3280,11074,11076" >&5 $as_echo_n "checking whether C compiler accepts -diag-disable 188,1684,2259,2304,3280,11074,11076... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -diag-disable 188,1684,2259,2304,3280,11074,11076" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -diag-disable 188,1684,2259,2304,3280,11074,11076" CXX_WFLAGS="$CXX_WFLAGS -diag-disable 188,1684,2259,2304,3280,11074,11076" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wextra" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wextra" >&5 $as_echo_n "checking whether C compiler accepts -Wextra... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wextra" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wextra" CXX_WFLAGS="$CXX_WFLAGS -Wextra" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Winit-self" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Winit-self" >&5 $as_echo_n "checking whether C compiler accepts -Winit-self... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Winit-self" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Winit-self" CXX_WFLAGS="$CXX_WFLAGS -Winit-self" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wunused-parameter" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wunused-parameter" >&5 $as_echo_n "checking whether C compiler accepts -Wunused-parameter... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wunused-parameter" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wunused-parameter" CXX_WFLAGS="$CXX_WFLAGS -Wunused-parameter" NO_UNUSED_PARAMETER_FLAG="-Wno-unused-parameter" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wmissing-prototypes" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-prototypes" >&5 $as_echo_n "checking whether C compiler accepts -Wmissing-prototypes... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wmissing-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wmissing-prototypes" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wmissing-declarations" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wmissing-declarations" >&5 $as_echo_n "checking whether C compiler accepts -Wmissing-declarations... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wmissing-declarations" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wmissing-declarations" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wformat" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat" >&5 $as_echo_n "checking whether C compiler accepts -Wformat... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wformat" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wformat" CXX_WFLAGS="$CXX_WFLAGS -Wformat" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wformat -Werror=format-security -Wno-format-nonliteral" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wformat -Werror=format-security -Wno-format-nonliteral" >&5 $as_echo_n "checking whether C compiler accepts -Wformat -Werror=format-security -Wno-format-nonliteral... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wformat -Werror=format-security -Wno-format-nonliteral" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Werror=format-security -Wno-format-nonliteral" CXX_WFLAGS="$CXX_WFLAGS -Werror=format-security -Wno-format-nonliteral" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wshorten-64-to-32" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshorten-64-to-32" >&5 $as_echo_n "checking whether C compiler accepts -Wshorten-64-to-32... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wshorten-64-to-32" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wshorten-64-to-32" CXX_WFLAGS="$CXX_WFLAGS -Wshorten-64-to-32" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wlogical-op" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wlogical-op" >&5 $as_echo_n "checking whether C compiler accepts -Wlogical-op... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wlogical-op" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wlogical-op" CXX_WFLAGS="$CXX_WFLAGS -Wlogical-op" NO_LOGICAL_OP_FLAG="-Wno-logical-op" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wshadow" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wshadow" >&5 $as_echo_n "checking whether C compiler accepts -Wshadow... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wshadow" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wshadow" CXX_WFLAGS="$CXX_WFLAGS -Wshadow" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Werror=vla" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror=vla" >&5 $as_echo_n "checking whether C compiler accepts -Werror=vla... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Werror=vla" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Werror=vla" CXX_WFLAGS="$CXX_WFLAGS -Werror=vla" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Werror=declaration-after-statement" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Werror=declaration-after-statement" >&5 $as_echo_n "checking whether C compiler accepts -Werror=declaration-after-statement... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Werror=declaration-after-statement" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wdeclaration-after-statement" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wdate-time" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdate-time" >&5 $as_echo_n "checking whether C compiler accepts -Wdate-time... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wdate-time" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wdate-time" CXX_WFLAGS="$CXX_WFLAGS -Wdate-time" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wnull-dereference" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wnull-dereference" >&5 $as_echo_n "checking whether C compiler accepts -Wnull-dereference... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wnull-dereference" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wnull-dereference" CXX_WFLAGS="$CXX_WFLAGS -Wnull-dereference" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wduplicated-cond" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wduplicated-cond" >&5 $as_echo_n "checking whether C compiler accepts -Wduplicated-cond... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wduplicated-cond" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wduplicated-cond" CXX_WFLAGS="$CXX_WFLAGS -Wduplicated-cond" else : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wextra-semi" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wextra-semi" >&5 $as_echo_n "checking whether C++ compiler accepts -Wextra-semi... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wextra-semi" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wextra-semi" else : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wno-sign-compare" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wno-sign-compare" >&5 $as_echo_n "checking whether C compiler accepts -Wno-sign-compare... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wno-sign-compare" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : NO_SIGN_COMPARE="-Wno-sign-compare" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wcomma" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wcomma" >&5 $as_echo_n "checking whether C compiler accepts -Wcomma... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wcomma" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wcomma" CXX_WFLAGS="$CXX_WFLAGS -Wcomma" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wfloat-conversion" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wfloat-conversion" >&5 $as_echo_n "checking whether C compiler accepts -Wfloat-conversion... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wfloat-conversion" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wfloat-conversion" CXX_WFLAGS="$CXX_WFLAGS -Wfloat-conversion" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wdocumentation -Wno-documentation-deprecated-sync" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wdocumentation -Wno-documentation-deprecated-sync" >&5 $as_echo_n "checking whether C compiler accepts -Wdocumentation -Wno-documentation-deprecated-sync... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wdocumentation -Wno-documentation-deprecated-sync" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : C_WFLAGS="$C_WFLAGS -Wdocumentation -Wno-documentation-deprecated-sync" CXX_WFLAGS="$CXX_WFLAGS -Wdocumentation -Wno-documentation-deprecated-sync" else : fi ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wunused-private-field" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wunused-private-field" >&5 $as_echo_n "checking whether C++ compiler accepts -Wunused-private-field... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wunused-private-field" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wunused-private-field" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wmissing-prototypes" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wmissing-prototypes" >&5 $as_echo_n "checking whether C++ compiler accepts -Wmissing-prototypes... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wmissing-prototypes" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wmissing-prototypes" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wmissing-declarations" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wmissing-declarations" >&5 $as_echo_n "checking whether C++ compiler accepts -Wmissing-declarations... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wmissing-declarations" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wmissing-declarations" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wnon-virtual-dtor" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wnon-virtual-dtor" >&5 $as_echo_n "checking whether C++ compiler accepts -Wnon-virtual-dtor... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wnon-virtual-dtor" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wnon-virtual-dtor" NO_NON_VIRTUAL_DTOR_FLAG="-Wno-non-virtual-dtor" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wold-style-cast" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wold-style-cast" >&5 $as_echo_n "checking whether C++ compiler accepts -Wold-style-cast... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wold-style-cast" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : WARN_OLD_STYLE_CAST="-Wold-style-cast" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Weffc++" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Weffc++" >&5 $as_echo_n "checking whether C++ compiler accepts -Weffc++... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Weffc++" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : WARN_EFFCPLUSPLUS="-Weffc++" else : fi if test "$WARN_EFFCPLUSPLUS" != ""; then SAVED_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $WARN_EFFCPLUSPLUS -Werror" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -Weffc++ should be enabled" >&5 $as_echo_n "checking if -Weffc++ should be enabled... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ class Base {}; class A: public Base {}; int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else WARN_EFFCPLUSPLUS="" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS="$SAVED_CXXFLAGS" CXX_WFLAGS="$CXX_WFLAGS $WARN_EFFCPLUSPLUS" fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Woverloaded-virtual" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Woverloaded-virtual" >&5 $as_echo_n "checking whether C++ compiler accepts -Woverloaded-virtual... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Woverloaded-virtual" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Woverloaded-virtual" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wweak-vtables" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wweak-vtables" >&5 $as_echo_n "checking whether C++ compiler accepts -Wweak-vtables... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wweak-vtables" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wweak-vtables" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wdeprecated" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wdeprecated" >&5 $as_echo_n "checking whether C++ compiler accepts -Wdeprecated... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wdeprecated" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wdeprecated" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wabstract-vbase-init" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wabstract-vbase-init" >&5 $as_echo_n "checking whether C++ compiler accepts -Wabstract-vbase-init... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wabstract-vbase-init" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wabstract-vbase-init" else : fi as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Winconsistent-missing-destructor-override" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Winconsistent-missing-destructor-override" >&5 $as_echo_n "checking whether C++ compiler accepts -Winconsistent-missing-destructor-override... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Winconsistent-missing-destructor-override" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Winconsistent-missing-destructor-override" else : fi HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT=no as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wzero-as-null-pointer-constant" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Wzero-as-null-pointer-constant" >&5 $as_echo_n "checking whether C++ compiler accepts -Wzero-as-null-pointer-constant... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wzero-as-null-pointer-constant" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wzero-as-null-pointer-constant" HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT=yes NO_ZERO_AS_NULL_POINTER_CONSTANT_FLAG="-Wno-zero-as-null-pointer-constant" else : fi if test "$HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT" = "yes"; then cat >>confdefs.h <<_ACEOF #define HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT 1 _ACEOF fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking to enable LTO (link time optimization) build" >&5 $as_echo_n "checking to enable LTO (link time optimization) build... " >&6; } # Check whether --enable-lto was given. if test "${enable_lto+set}" = set; then : enableval=$enable_lto; fi FLTO_FLAG="" if test "x$enable_lto" = "xyes"; then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_$ERROR_ON_UNKNOWN_OPTIONS_-flto" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -flto" >&5 $as_echo_n "checking whether C++ compiler accepts -flto... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CXXFLAGS CXXFLAGS="$CXXFLAGS $ERROR_ON_UNKNOWN_OPTIONS -flto" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXXFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : FLTO_FLAG="-flto" else : fi if test "$FLTO_FLAG" != ""; then SAVED_CXXFLAGS=$CXXFLAGS CXXFLAGS="$CXXFLAGS $FLTO_FLAG -Werror" { $as_echo "$as_me:${as_lineno-$LINENO}: checking if -flto is available at link time" >&5 $as_echo_n "checking if -flto is available at link time... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_link "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else FLTO_FLAG="" { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CXXFLAGS="$SAVED_CXXFLAGS" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu as_CACHEVAR=`$as_echo "ax_cv_check_cflags_$ERROR_ON_UNKNOWN_OPTIONS_-Wodr" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -Wodr" >&5 $as_echo_n "checking whether C compiler accepts -Wodr... " >&6; } if eval \${$as_CACHEVAR+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS $ERROR_ON_UNKNOWN_OPTIONS -Wodr" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$as_CACHEVAR=yes" else eval "$as_CACHEVAR=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi eval ac_res=\$$as_CACHEVAR { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : CXX_WFLAGS="$CXX_WFLAGS -Wodr" else : fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi FLTO_FLAG=$FLTO_FLAG C_WFLAGS=$C_WFLAGS CXX_WFLAGS=$CXX_WFLAGS NO_ZERO_AS_NULL_POINTER_CONSTANT_FLAG=$NO_ZERO_AS_NULL_POINTER_CONSTANT_FLAG CFLAGS="${CFLAGS} -fvisibility=hidden" CXXFLAGS="${CXXFLAGS} -fvisibility=hidden" save_CFLAGS="$CFLAGS" CFLAGS=`echo "$CFLAGS" | sed "s/-Werror/ /"` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for exp in -lm" >&5 $as_echo_n "checking for exp in -lm... " >&6; } if ${ac_cv_lib_m_exp+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lm $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char exp (); int main () { return exp (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_m_exp=yes else ac_cv_lib_m_exp=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_exp" >&5 $as_echo "$ac_cv_lib_m_exp" >&6; } if test "x$ac_cv_lib_m_exp" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF LIBS="-lm $LIBS" fi CFLAGS="$save_CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C compiler accepts -fp-model precise" >&5 $as_echo_n "checking whether C compiler accepts -fp-model precise... " >&6; } if ${ax_cv_check_cflags__Werror__fp_model_precise+:} false; then : $as_echo_n "(cached) " >&6 else ax_check_save_flags=$CFLAGS CFLAGS="$CFLAGS -Werror -fp-model precise" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ax_cv_check_cflags__Werror__fp_model_precise=yes else ax_cv_check_cflags__Werror__fp_model_precise=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS=$ax_check_save_flags fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cflags__Werror__fp_model_precise" >&5 $as_echo "$ax_cv_check_cflags__Werror__fp_model_precise" >&6; } if test "x$ax_cv_check_cflags__Werror__fp_model_precise" = xyes; then : CFLAGS="$CFLAGS -fp-model precise" else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing sqrt" >&5 $as_echo_n "checking for library containing sqrt... " >&6; } if ${ac_cv_search_sqrt+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char sqrt (); int main () { return sqrt (); ; return 0; } _ACEOF for ac_lib in '' m; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_sqrt=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_sqrt+:} false; then : break fi done if ${ac_cv_search_sqrt+:} false; then : else ac_cv_search_sqrt=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sqrt" >&5 $as_echo "$ac_cv_search_sqrt" >&6; } ac_res=$ac_cv_search_sqrt if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_fn_c_check_func "$LINENO" "localeconv" "ac_cv_func_localeconv" if test "x$ac_cv_func_localeconv" = xyes; then : $as_echo "#define HAVE_LOCALECONV 1" >>confdefs.h fi for ac_func in strerror do : ac_fn_c_check_func "$LINENO" "strerror" "ac_cv_func_strerror" if test "x$ac_cv_func_strerror" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRERROR 1 _ACEOF fi done JNI_INCLUDE= export JNI_INCLUDE # Check whether --with-jni was given. if test "${with_jni+set}" = set; then : withval=$with_jni; fi if test "$with_jni" = "yes" ; then $as_echo "#define JNI_ENABLED 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable Java/JNI support" >&5 $as_echo_n "checking whether to enable Java/JNI support... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 $as_echo "enabled" >&6; } elif test "$with_jni" != "no" -a "$with_jni" != "" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable Java/JNI support" >&5 $as_echo_n "checking whether to enable Java/JNI support... " >&6; } if test \! -r "$with_jni/jni.h" ; then as_fn_error $? "Did not find $with_jni/jni.h" "$LINENO" 5 fi $as_echo "#define JNI_ENABLED 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 $as_echo "enabled" >&6; } JNI_INCLUDE="-I$with_jni" elif test "$with_jni" = "" ; then for ac_header in jni.h do : ac_fn_c_check_header_mongrel "$LINENO" "jni.h" "ac_cv_header_jni_h" "$ac_includes_default" if test "x$ac_cv_header_jni_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_JNI_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable Java/JNI support" >&5 $as_echo_n "checking whether to enable Java/JNI support... " >&6; } if test "$ac_cv_header_jni_h" = "no" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 $as_echo "disabled" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 $as_echo "enabled" >&6; } $as_echo "#define JNI_ENABLED 1" >>confdefs.h fi else { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable Java/JNI support" >&5 $as_echo_n "checking whether to enable Java/JNI support... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 $as_echo "disabled" >&6; } fi JNI_INCLUDE=$JNI_INCLUDE # Check whether --with-mutex was given. if test "${with_mutex+set}" = set; then : withval=$with_mutex; fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mutexes" >&5 $as_echo_n "checking for mutexes... " >&6; } THREAD_LIB="" if test "$with_mutex" = yes -o x"$with_mutex" = x ; then MUTEX_SETTING=pthread { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 $as_echo_n "checking for pthread_create in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_create (); int main () { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_create=yes else ac_cv_lib_pthread_pthread_create=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 $as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : PTHREAD_EXISTS=YES fi if test -n "$PTHREAD_EXISTS" ; then THREAD_LIB="-lpthread" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_mutexattr_settype in -lpthread" >&5 $as_echo_n "checking for pthread_mutexattr_settype in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_mutexattr_settype+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_mutexattr_settype (); int main () { return pthread_mutexattr_settype (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_mutexattr_settype=yes else ac_cv_lib_pthread_pthread_mutexattr_settype=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_mutexattr_settype" >&5 $as_echo "$ac_cv_lib_pthread_pthread_mutexattr_settype" >&6; } if test "x$ac_cv_lib_pthread_pthread_mutexattr_settype" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" fi ac_fn_c_check_decl "$LINENO" "PTHREAD_MUTEX_RECURSIVE" "ac_cv_have_decl_PTHREAD_MUTEX_RECURSIVE" "#include " if test "x$ac_cv_have_decl_PTHREAD_MUTEX_RECURSIVE" = xyes; then : $as_echo "#define HAVE_PTHREAD_MUTEX_RECURSIVE /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled, pthread" >&5 $as_echo "enabled, pthread" >&6; } else MUTEX_SETTING=stub { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled by user" >&5 $as_echo "disabled by user" >&6; } fi MUTEX_SETTING=$MUTEX_SETTING THREAD_LIB=$THREAD_LIB if test "x$SQLITE3_CFLAGS$SQLITE3_LIBS" = "x" ; then if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.9.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SQLITE3" >&5 $as_echo_n "checking for SQLITE3... " >&6; } if test -n "$PKG_CONFIG"; then if test -n "$SQLITE3_CFLAGS"; then pkg_cv_SQLITE3_CFLAGS="$SQLITE3_CFLAGS" else if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sqlite3 >= 3.11\""; } >&5 ($PKG_CONFIG --exists --print-errors "sqlite3 >= 3.11") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SQLITE3_CFLAGS=`$PKG_CONFIG --cflags "sqlite3 >= 3.11" 2>/dev/null` else pkg_failed=yes fi fi else pkg_failed=untried fi if test -n "$PKG_CONFIG"; then if test -n "$SQLITE3_LIBS"; then pkg_cv_SQLITE3_LIBS="$SQLITE3_LIBS" else if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"sqlite3 >= 3.11\""; } >&5 ($PKG_CONFIG --exists --print-errors "sqlite3 >= 3.11") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_SQLITE3_LIBS=`$PKG_CONFIG --libs "sqlite3 >= 3.11" 2>/dev/null` else pkg_failed=yes fi fi else pkg_failed=untried fi if test $pkg_failed = yes; then if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then SQLITE3_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "sqlite3 >= 3.11"` else SQLITE3_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "sqlite3 >= 3.11"` fi # Put the nasty error message in config.log where it belongs echo "$SQLITE3_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (sqlite3 >= 3.11) were not met: $SQLITE3_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables SQLITE3_CFLAGS and SQLITE3_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. " "$LINENO" 5 elif test $pkg_failed = untried; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables SQLITE3_CFLAGS and SQLITE3_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else SQLITE3_CFLAGS=$pkg_cv_SQLITE3_CFLAGS SQLITE3_LIBS=$pkg_cv_SQLITE3_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } : fi fi SQLITE3_CFLAGS=$SQLITE3_CFLAGS SQLITE3_LIBS=$SQLITE3_LIBS # Extract the first word of "sqlite3", so it can be a program name with args. set dummy sqlite3; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_SQLITE3_CHECK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$SQLITE3_CHECK"; then ac_cv_prog_SQLITE3_CHECK="$SQLITE3_CHECK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_SQLITE3_CHECK="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi SQLITE3_CHECK=$ac_cv_prog_SQLITE3_CHECK if test -n "$SQLITE3_CHECK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SQLITE3_CHECK" >&5 $as_echo "$SQLITE3_CHECK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test x"$SQLITE3_CHECK" != x"yes" ; then as_fn_error $? "Please install sqlite3 binary." "$LINENO" 5 fi # Check whether --with-external-gtest was given. if test "${with_external_gtest+set}" = set; then : withval=$with_external_gtest; fi if test "x$with_external_gtest" = "xyes" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: using external GTest." >&5 $as_echo "using external GTest." >&6; } pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTEST" >&5 $as_echo_n "checking for GTEST... " >&6; } if test -n "$PKG_CONFIG"; then if test -n "$GTEST_CFLAGS"; then pkg_cv_GTEST_CFLAGS="$GTEST_CFLAGS" else if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtest >= 1.8.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gtest >= 1.8.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTEST_CFLAGS=`$PKG_CONFIG --cflags "gtest >= 1.8.0" 2>/dev/null` else pkg_failed=yes fi fi else pkg_failed=untried fi if test -n "$PKG_CONFIG"; then if test -n "$GTEST_LIBS"; then pkg_cv_GTEST_LIBS="$GTEST_LIBS" else if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtest >= 1.8.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gtest >= 1.8.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTEST_LIBS=`$PKG_CONFIG --libs "gtest >= 1.8.0" 2>/dev/null` else pkg_failed=yes fi fi else pkg_failed=untried fi if test $pkg_failed = yes; then if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then GTEST_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "gtest >= 1.8.0"` else GTEST_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gtest >= 1.8.0"` fi # Put the nasty error message in config.log where it belongs echo "$GTEST_PKG_ERRORS" >&5 as_fn_error $? "Package requirements (gtest >= 1.8.0) were not met: $GTEST_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. Alternatively, you may set the environment variables GTEST_CFLAGS and GTEST_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. " "$LINENO" 5 elif test $pkg_failed = untried; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. Alternatively, you may set the environment variables GTEST_CFLAGS and GTEST_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details. To get pkg-config, see . See \`config.log' for more details" "$LINENO" 5; } else GTEST_CFLAGS=$pkg_cv_GTEST_CFLAGS GTEST_LIBS=$pkg_cv_GTEST_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } : fi else { $as_echo "$as_me:${as_lineno-$LINENO}: result: using internal GTest." >&5 $as_echo "using internal GTest." >&6; } GTEST_CFLAGS="-I\$(top_srcdir)/test/googletest/include" GTEST_LIBS="\$(top_builddir)/test/googletest/libgtest.la" fi if test "x$with_external_gtest" = "xyes"; then USE_EXTERNAL_GTEST_TRUE= USE_EXTERNAL_GTEST_FALSE='#' else USE_EXTERNAL_GTEST_TRUE='#' USE_EXTERNAL_GTEST_FALSE= fi GTEST_CFLAGS=$GTEST_CFLAGS GTEST_LIBS=$GTEST_LIBS ac_config_files="$ac_config_files Makefile cmake/Makefile src/Makefile include/Makefile include/proj/Makefile include/proj/internal/Makefile include/proj/internal/nlohmann/Makefile test/Makefile test/cli/Makefile test/gie/Makefile test/gigs/Makefile test/unit/Makefile man/Makefile man/man1/Makefile man/man3/Makefile data/Makefile jniwrap/Makefile jniwrap/org.osgeo.proj/Makefile jniwrap/org.osgeo.proj/org/Makefile jniwrap/org.osgeo.proj/org/proj4/Makefile" if ! test "x$with_external_gtest" = "xyes" ; then ac_config_files="$ac_config_files test/googletest/Makefile test/googletest/include/Makefile test/googletest/include/gtest/Makefile test/googletest/include/gtest/internal/Makefile test/googletest/include/gtest/internal/custom/Makefile test/googletest/src/Makefile" fi ac_config_files="$ac_config_files proj.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${USE_EXTERNAL_GTEST_TRUE}" && test -z "${USE_EXTERNAL_GTEST_FALSE}"; then as_fn_error $? "conditional \"USE_EXTERNAL_GTEST\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by PROJ $as_me 6.3.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to . PROJ home page: ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ PROJ config.status 6.3.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' macro_version='`$ECHO "$macro_version" | $SED "$delay_single_quote_subst"`' macro_revision='`$ECHO "$macro_revision" | $SED "$delay_single_quote_subst"`' enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`' enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`' pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`' enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`' shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`' SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`' ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`' PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`' host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`' host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`' host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`' build_alias='`$ECHO "$build_alias" | $SED "$delay_single_quote_subst"`' build='`$ECHO "$build" | $SED "$delay_single_quote_subst"`' build_os='`$ECHO "$build_os" | $SED "$delay_single_quote_subst"`' SED='`$ECHO "$SED" | $SED "$delay_single_quote_subst"`' Xsed='`$ECHO "$Xsed" | $SED "$delay_single_quote_subst"`' GREP='`$ECHO "$GREP" | $SED "$delay_single_quote_subst"`' EGREP='`$ECHO "$EGREP" | $SED "$delay_single_quote_subst"`' FGREP='`$ECHO "$FGREP" | $SED "$delay_single_quote_subst"`' LD='`$ECHO "$LD" | $SED "$delay_single_quote_subst"`' NM='`$ECHO "$NM" | $SED "$delay_single_quote_subst"`' LN_S='`$ECHO "$LN_S" | $SED "$delay_single_quote_subst"`' max_cmd_len='`$ECHO "$max_cmd_len" | $SED "$delay_single_quote_subst"`' ac_objext='`$ECHO "$ac_objext" | $SED "$delay_single_quote_subst"`' exeext='`$ECHO "$exeext" | $SED "$delay_single_quote_subst"`' lt_unset='`$ECHO "$lt_unset" | $SED "$delay_single_quote_subst"`' lt_SP2NL='`$ECHO "$lt_SP2NL" | $SED "$delay_single_quote_subst"`' lt_NL2SP='`$ECHO "$lt_NL2SP" | $SED "$delay_single_quote_subst"`' lt_cv_to_host_file_cmd='`$ECHO "$lt_cv_to_host_file_cmd" | $SED "$delay_single_quote_subst"`' lt_cv_to_tool_file_cmd='`$ECHO "$lt_cv_to_tool_file_cmd" | $SED "$delay_single_quote_subst"`' reload_flag='`$ECHO "$reload_flag" | $SED "$delay_single_quote_subst"`' reload_cmds='`$ECHO "$reload_cmds" | $SED "$delay_single_quote_subst"`' OBJDUMP='`$ECHO "$OBJDUMP" | $SED "$delay_single_quote_subst"`' deplibs_check_method='`$ECHO "$deplibs_check_method" | $SED "$delay_single_quote_subst"`' file_magic_cmd='`$ECHO "$file_magic_cmd" | $SED "$delay_single_quote_subst"`' file_magic_glob='`$ECHO "$file_magic_glob" | $SED "$delay_single_quote_subst"`' want_nocaseglob='`$ECHO "$want_nocaseglob" | $SED "$delay_single_quote_subst"`' DLLTOOL='`$ECHO "$DLLTOOL" | $SED "$delay_single_quote_subst"`' sharedlib_from_linklib_cmd='`$ECHO "$sharedlib_from_linklib_cmd" | $SED "$delay_single_quote_subst"`' AR='`$ECHO "$AR" | $SED "$delay_single_quote_subst"`' AR_FLAGS='`$ECHO "$AR_FLAGS" | $SED "$delay_single_quote_subst"`' archiver_list_spec='`$ECHO "$archiver_list_spec" | $SED "$delay_single_quote_subst"`' STRIP='`$ECHO "$STRIP" | $SED "$delay_single_quote_subst"`' RANLIB='`$ECHO "$RANLIB" | $SED "$delay_single_quote_subst"`' old_postinstall_cmds='`$ECHO "$old_postinstall_cmds" | $SED "$delay_single_quote_subst"`' old_postuninstall_cmds='`$ECHO "$old_postuninstall_cmds" | $SED "$delay_single_quote_subst"`' old_archive_cmds='`$ECHO "$old_archive_cmds" | $SED "$delay_single_quote_subst"`' lock_old_archive_extraction='`$ECHO "$lock_old_archive_extraction" | $SED "$delay_single_quote_subst"`' CC='`$ECHO "$CC" | $SED "$delay_single_quote_subst"`' CFLAGS='`$ECHO "$CFLAGS" | $SED "$delay_single_quote_subst"`' compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`' GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`' lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`' lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`' nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`' lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`' lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`' objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`' MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic='`$ECHO "$lt_prog_compiler_pic" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl='`$ECHO "$lt_prog_compiler_wl" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static='`$ECHO "$lt_prog_compiler_static" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o='`$ECHO "$lt_cv_prog_compiler_c_o" | $SED "$delay_single_quote_subst"`' need_locks='`$ECHO "$need_locks" | $SED "$delay_single_quote_subst"`' MANIFEST_TOOL='`$ECHO "$MANIFEST_TOOL" | $SED "$delay_single_quote_subst"`' DSYMUTIL='`$ECHO "$DSYMUTIL" | $SED "$delay_single_quote_subst"`' NMEDIT='`$ECHO "$NMEDIT" | $SED "$delay_single_quote_subst"`' LIPO='`$ECHO "$LIPO" | $SED "$delay_single_quote_subst"`' OTOOL='`$ECHO "$OTOOL" | $SED "$delay_single_quote_subst"`' OTOOL64='`$ECHO "$OTOOL64" | $SED "$delay_single_quote_subst"`' libext='`$ECHO "$libext" | $SED "$delay_single_quote_subst"`' shrext_cmds='`$ECHO "$shrext_cmds" | $SED "$delay_single_quote_subst"`' extract_expsyms_cmds='`$ECHO "$extract_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc='`$ECHO "$archive_cmds_need_lc" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes='`$ECHO "$enable_shared_with_static_runtimes" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec='`$ECHO "$export_dynamic_flag_spec" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec='`$ECHO "$whole_archive_flag_spec" | $SED "$delay_single_quote_subst"`' compiler_needs_object='`$ECHO "$compiler_needs_object" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds='`$ECHO "$old_archive_from_new_cmds" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds='`$ECHO "$old_archive_from_expsyms_cmds" | $SED "$delay_single_quote_subst"`' archive_cmds='`$ECHO "$archive_cmds" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds='`$ECHO "$archive_expsym_cmds" | $SED "$delay_single_quote_subst"`' module_cmds='`$ECHO "$module_cmds" | $SED "$delay_single_quote_subst"`' module_expsym_cmds='`$ECHO "$module_expsym_cmds" | $SED "$delay_single_quote_subst"`' with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`' allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`' no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`' hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`' hardcode_minus_L='`$ECHO "$hardcode_minus_L" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var='`$ECHO "$hardcode_shlibpath_var" | $SED "$delay_single_quote_subst"`' hardcode_automatic='`$ECHO "$hardcode_automatic" | $SED "$delay_single_quote_subst"`' inherit_rpath='`$ECHO "$inherit_rpath" | $SED "$delay_single_quote_subst"`' link_all_deplibs='`$ECHO "$link_all_deplibs" | $SED "$delay_single_quote_subst"`' always_export_symbols='`$ECHO "$always_export_symbols" | $SED "$delay_single_quote_subst"`' export_symbols_cmds='`$ECHO "$export_symbols_cmds" | $SED "$delay_single_quote_subst"`' exclude_expsyms='`$ECHO "$exclude_expsyms" | $SED "$delay_single_quote_subst"`' include_expsyms='`$ECHO "$include_expsyms" | $SED "$delay_single_quote_subst"`' prelink_cmds='`$ECHO "$prelink_cmds" | $SED "$delay_single_quote_subst"`' postlink_cmds='`$ECHO "$postlink_cmds" | $SED "$delay_single_quote_subst"`' file_list_spec='`$ECHO "$file_list_spec" | $SED "$delay_single_quote_subst"`' variables_saved_for_relink='`$ECHO "$variables_saved_for_relink" | $SED "$delay_single_quote_subst"`' need_lib_prefix='`$ECHO "$need_lib_prefix" | $SED "$delay_single_quote_subst"`' need_version='`$ECHO "$need_version" | $SED "$delay_single_quote_subst"`' version_type='`$ECHO "$version_type" | $SED "$delay_single_quote_subst"`' runpath_var='`$ECHO "$runpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_var='`$ECHO "$shlibpath_var" | $SED "$delay_single_quote_subst"`' shlibpath_overrides_runpath='`$ECHO "$shlibpath_overrides_runpath" | $SED "$delay_single_quote_subst"`' libname_spec='`$ECHO "$libname_spec" | $SED "$delay_single_quote_subst"`' library_names_spec='`$ECHO "$library_names_spec" | $SED "$delay_single_quote_subst"`' soname_spec='`$ECHO "$soname_spec" | $SED "$delay_single_quote_subst"`' install_override_mode='`$ECHO "$install_override_mode" | $SED "$delay_single_quote_subst"`' postinstall_cmds='`$ECHO "$postinstall_cmds" | $SED "$delay_single_quote_subst"`' postuninstall_cmds='`$ECHO "$postuninstall_cmds" | $SED "$delay_single_quote_subst"`' finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`' finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`' hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`' sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`' configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`' configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`' hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`' enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`' enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`' enable_dlopen_self_static='`$ECHO "$enable_dlopen_self_static" | $SED "$delay_single_quote_subst"`' old_striplib='`$ECHO "$old_striplib" | $SED "$delay_single_quote_subst"`' striplib='`$ECHO "$striplib" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs='`$ECHO "$compiler_lib_search_dirs" | $SED "$delay_single_quote_subst"`' predep_objects='`$ECHO "$predep_objects" | $SED "$delay_single_quote_subst"`' postdep_objects='`$ECHO "$postdep_objects" | $SED "$delay_single_quote_subst"`' predeps='`$ECHO "$predeps" | $SED "$delay_single_quote_subst"`' postdeps='`$ECHO "$postdeps" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path='`$ECHO "$compiler_lib_search_path" | $SED "$delay_single_quote_subst"`' LD_CXX='`$ECHO "$LD_CXX" | $SED "$delay_single_quote_subst"`' reload_flag_CXX='`$ECHO "$reload_flag_CXX" | $SED "$delay_single_quote_subst"`' reload_cmds_CXX='`$ECHO "$reload_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_cmds_CXX='`$ECHO "$old_archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' compiler_CXX='`$ECHO "$compiler_CXX" | $SED "$delay_single_quote_subst"`' GCC_CXX='`$ECHO "$GCC_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "$lt_prog_compiler_no_builtin_flag_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_pic_CXX='`$ECHO "$lt_prog_compiler_pic_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_wl_CXX='`$ECHO "$lt_prog_compiler_wl_CXX" | $SED "$delay_single_quote_subst"`' lt_prog_compiler_static_CXX='`$ECHO "$lt_prog_compiler_static_CXX" | $SED "$delay_single_quote_subst"`' lt_cv_prog_compiler_c_o_CXX='`$ECHO "$lt_cv_prog_compiler_c_o_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_need_lc_CXX='`$ECHO "$archive_cmds_need_lc_CXX" | $SED "$delay_single_quote_subst"`' enable_shared_with_static_runtimes_CXX='`$ECHO "$enable_shared_with_static_runtimes_CXX" | $SED "$delay_single_quote_subst"`' export_dynamic_flag_spec_CXX='`$ECHO "$export_dynamic_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' whole_archive_flag_spec_CXX='`$ECHO "$whole_archive_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' compiler_needs_object_CXX='`$ECHO "$compiler_needs_object_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_new_cmds_CXX='`$ECHO "$old_archive_from_new_cmds_CXX" | $SED "$delay_single_quote_subst"`' old_archive_from_expsyms_cmds_CXX='`$ECHO "$old_archive_from_expsyms_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_cmds_CXX='`$ECHO "$archive_cmds_CXX" | $SED "$delay_single_quote_subst"`' archive_expsym_cmds_CXX='`$ECHO "$archive_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_cmds_CXX='`$ECHO "$module_cmds_CXX" | $SED "$delay_single_quote_subst"`' module_expsym_cmds_CXX='`$ECHO "$module_expsym_cmds_CXX" | $SED "$delay_single_quote_subst"`' with_gnu_ld_CXX='`$ECHO "$with_gnu_ld_CXX" | $SED "$delay_single_quote_subst"`' allow_undefined_flag_CXX='`$ECHO "$allow_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' no_undefined_flag_CXX='`$ECHO "$no_undefined_flag_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_flag_spec_CXX='`$ECHO "$hardcode_libdir_flag_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_libdir_separator_CXX='`$ECHO "$hardcode_libdir_separator_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_CXX='`$ECHO "$hardcode_direct_CXX" | $SED "$delay_single_quote_subst"`' hardcode_direct_absolute_CXX='`$ECHO "$hardcode_direct_absolute_CXX" | $SED "$delay_single_quote_subst"`' hardcode_minus_L_CXX='`$ECHO "$hardcode_minus_L_CXX" | $SED "$delay_single_quote_subst"`' hardcode_shlibpath_var_CXX='`$ECHO "$hardcode_shlibpath_var_CXX" | $SED "$delay_single_quote_subst"`' hardcode_automatic_CXX='`$ECHO "$hardcode_automatic_CXX" | $SED "$delay_single_quote_subst"`' inherit_rpath_CXX='`$ECHO "$inherit_rpath_CXX" | $SED "$delay_single_quote_subst"`' link_all_deplibs_CXX='`$ECHO "$link_all_deplibs_CXX" | $SED "$delay_single_quote_subst"`' always_export_symbols_CXX='`$ECHO "$always_export_symbols_CXX" | $SED "$delay_single_quote_subst"`' export_symbols_cmds_CXX='`$ECHO "$export_symbols_cmds_CXX" | $SED "$delay_single_quote_subst"`' exclude_expsyms_CXX='`$ECHO "$exclude_expsyms_CXX" | $SED "$delay_single_quote_subst"`' include_expsyms_CXX='`$ECHO "$include_expsyms_CXX" | $SED "$delay_single_quote_subst"`' prelink_cmds_CXX='`$ECHO "$prelink_cmds_CXX" | $SED "$delay_single_quote_subst"`' postlink_cmds_CXX='`$ECHO "$postlink_cmds_CXX" | $SED "$delay_single_quote_subst"`' file_list_spec_CXX='`$ECHO "$file_list_spec_CXX" | $SED "$delay_single_quote_subst"`' hardcode_action_CXX='`$ECHO "$hardcode_action_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_dirs_CXX='`$ECHO "$compiler_lib_search_dirs_CXX" | $SED "$delay_single_quote_subst"`' predep_objects_CXX='`$ECHO "$predep_objects_CXX" | $SED "$delay_single_quote_subst"`' postdep_objects_CXX='`$ECHO "$postdep_objects_CXX" | $SED "$delay_single_quote_subst"`' predeps_CXX='`$ECHO "$predeps_CXX" | $SED "$delay_single_quote_subst"`' postdeps_CXX='`$ECHO "$postdeps_CXX" | $SED "$delay_single_quote_subst"`' compiler_lib_search_path_CXX='`$ECHO "$compiler_lib_search_path_CXX" | $SED "$delay_single_quote_subst"`' LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$1 _LTECHO_EOF' } # Quote evaled strings. for var in SHELL \ ECHO \ PATH_SEPARATOR \ SED \ GREP \ EGREP \ FGREP \ LD \ NM \ LN_S \ lt_SP2NL \ lt_NL2SP \ reload_flag \ OBJDUMP \ deplibs_check_method \ file_magic_cmd \ file_magic_glob \ want_nocaseglob \ DLLTOOL \ sharedlib_from_linklib_cmd \ AR \ AR_FLAGS \ archiver_list_spec \ STRIP \ RANLIB \ CC \ CFLAGS \ compiler \ lt_cv_sys_global_symbol_pipe \ lt_cv_sys_global_symbol_to_cdecl \ lt_cv_sys_global_symbol_to_import \ lt_cv_sys_global_symbol_to_c_name_address \ lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ lt_cv_nm_interface \ nm_file_list_spec \ lt_cv_truncate_bin \ lt_prog_compiler_no_builtin_flag \ lt_prog_compiler_pic \ lt_prog_compiler_wl \ lt_prog_compiler_static \ lt_cv_prog_compiler_c_o \ need_locks \ MANIFEST_TOOL \ DSYMUTIL \ NMEDIT \ LIPO \ OTOOL \ OTOOL64 \ shrext_cmds \ export_dynamic_flag_spec \ whole_archive_flag_spec \ compiler_needs_object \ with_gnu_ld \ allow_undefined_flag \ no_undefined_flag \ hardcode_libdir_flag_spec \ hardcode_libdir_separator \ exclude_expsyms \ include_expsyms \ file_list_spec \ variables_saved_for_relink \ libname_spec \ library_names_spec \ soname_spec \ install_override_mode \ finish_eval \ old_striplib \ striplib \ compiler_lib_search_dirs \ predep_objects \ postdep_objects \ predeps \ postdeps \ compiler_lib_search_path \ LD_CXX \ reload_flag_CXX \ compiler_CXX \ lt_prog_compiler_no_builtin_flag_CXX \ lt_prog_compiler_pic_CXX \ lt_prog_compiler_wl_CXX \ lt_prog_compiler_static_CXX \ lt_cv_prog_compiler_c_o_CXX \ export_dynamic_flag_spec_CXX \ whole_archive_flag_spec_CXX \ compiler_needs_object_CXX \ with_gnu_ld_CXX \ allow_undefined_flag_CXX \ no_undefined_flag_CXX \ hardcode_libdir_flag_spec_CXX \ hardcode_libdir_separator_CXX \ exclude_expsyms_CXX \ include_expsyms_CXX \ file_list_spec_CXX \ compiler_lib_search_dirs_CXX \ predep_objects_CXX \ postdep_objects_CXX \ predeps_CXX \ postdeps_CXX \ compiler_lib_search_path_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in reload_cmds \ old_postinstall_cmds \ old_postuninstall_cmds \ old_archive_cmds \ extract_expsyms_cmds \ old_archive_from_new_cmds \ old_archive_from_expsyms_cmds \ archive_cmds \ archive_expsym_cmds \ module_cmds \ module_expsym_cmds \ export_symbols_cmds \ prelink_cmds \ postlink_cmds \ postinstall_cmds \ postuninstall_cmds \ finish_cmds \ sys_lib_search_path_spec \ configure_time_dlsearch_path \ configure_time_lt_sys_library_path \ reload_cmds_CXX \ old_archive_cmds_CXX \ old_archive_from_new_cmds_CXX \ old_archive_from_expsyms_cmds_CXX \ archive_cmds_CXX \ archive_expsym_cmds_CXX \ module_cmds_CXX \ module_expsym_cmds_CXX \ export_symbols_cmds_CXX \ prelink_cmds_CXX \ postlink_cmds_CXX; do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[\\\\\\\`\\"\\\$]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done ac_aux_dir='$ac_aux_dir' # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile' _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/proj_config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/proj_config.h" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "cmake/Makefile") CONFIG_FILES="$CONFIG_FILES cmake/Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "include/Makefile") CONFIG_FILES="$CONFIG_FILES include/Makefile" ;; "include/proj/Makefile") CONFIG_FILES="$CONFIG_FILES include/proj/Makefile" ;; "include/proj/internal/Makefile") CONFIG_FILES="$CONFIG_FILES include/proj/internal/Makefile" ;; "include/proj/internal/nlohmann/Makefile") CONFIG_FILES="$CONFIG_FILES include/proj/internal/nlohmann/Makefile" ;; "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; "test/cli/Makefile") CONFIG_FILES="$CONFIG_FILES test/cli/Makefile" ;; "test/gie/Makefile") CONFIG_FILES="$CONFIG_FILES test/gie/Makefile" ;; "test/gigs/Makefile") CONFIG_FILES="$CONFIG_FILES test/gigs/Makefile" ;; "test/unit/Makefile") CONFIG_FILES="$CONFIG_FILES test/unit/Makefile" ;; "man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;; "man/man1/Makefile") CONFIG_FILES="$CONFIG_FILES man/man1/Makefile" ;; "man/man3/Makefile") CONFIG_FILES="$CONFIG_FILES man/man3/Makefile" ;; "data/Makefile") CONFIG_FILES="$CONFIG_FILES data/Makefile" ;; "jniwrap/Makefile") CONFIG_FILES="$CONFIG_FILES jniwrap/Makefile" ;; "jniwrap/org.osgeo.proj/Makefile") CONFIG_FILES="$CONFIG_FILES jniwrap/org.osgeo.proj/Makefile" ;; "jniwrap/org.osgeo.proj/org/Makefile") CONFIG_FILES="$CONFIG_FILES jniwrap/org.osgeo.proj/org/Makefile" ;; "jniwrap/org.osgeo.proj/org/proj4/Makefile") CONFIG_FILES="$CONFIG_FILES jniwrap/org.osgeo.proj/org/proj4/Makefile" ;; "test/googletest/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/Makefile" ;; "test/googletest/include/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/include/Makefile" ;; "test/googletest/include/gtest/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/include/gtest/Makefile" ;; "test/googletest/include/gtest/internal/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/include/gtest/internal/Makefile" ;; "test/googletest/include/gtest/internal/custom/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/include/gtest/internal/custom/Makefile" ;; "test/googletest/src/Makefile") CONFIG_FILES="$CONFIG_FILES test/googletest/src/Makefile" ;; "proj.pc") CONFIG_FILES="$CONFIG_FILES proj.pc" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; "libtool":C) # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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 2 of of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool 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 . # The names of the tagged configurations supported by this script. available_tags='CXX ' # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG # Which release of libtool.m4 was used? macro_version=$macro_version macro_revision=$macro_revision # Whether or not to build shared libraries. build_libtool_libs=$enable_shared # Whether or not to build static libraries. build_old_libs=$enable_static # What type of objects to build. pic_mode=$pic_mode # Whether or not to optimize for fast installation. fast_install=$enable_fast_install # Shared archive member basename,for filename based shared library versioning on AIX. shared_archive_member_spec=$shared_archive_member_spec # Shell to use when invoking shell scripts. SHELL=$lt_SHELL # An echo program that protects backslashes. ECHO=$lt_ECHO # The PATH separator for the build system. PATH_SEPARATOR=$lt_PATH_SEPARATOR # The host system. host_alias=$host_alias host=$host host_os=$host_os # The build system. build_alias=$build_alias build=$build build_os=$build_os # A sed program that does not truncate output. SED=$lt_SED # Sed that helps us avoid accidentally triggering echo(1) options like -n. Xsed="\$SED -e 1s/^X//" # A grep program that handles long lines. GREP=$lt_GREP # An ERE matcher. EGREP=$lt_EGREP # A literal string matcher. FGREP=$lt_FGREP # A BSD- or MS-compatible name lister. NM=$lt_NM # Whether we need soft or hard links. LN_S=$lt_LN_S # What is the maximum length of a command? max_cmd_len=$max_cmd_len # Object file suffix (normally "o"). objext=$ac_objext # Executable file suffix (normally ""). exeext=$exeext # whether the shell understands "unset". lt_unset=$lt_unset # turn spaces into newlines. SP2NL=$lt_lt_SP2NL # turn newlines into spaces. NL2SP=$lt_lt_NL2SP # convert \$build file names to \$host format. to_host_file_cmd=$lt_cv_to_host_file_cmd # convert \$build files to toolchain format. to_tool_file_cmd=$lt_cv_to_tool_file_cmd # An object symbol dumper. OBJDUMP=$lt_OBJDUMP # Method to check whether dependent libraries are shared objects. deplibs_check_method=$lt_deplibs_check_method # Command to use when deplibs_check_method = "file_magic". file_magic_cmd=$lt_file_magic_cmd # How to find potential files when deplibs_check_method = "file_magic". file_magic_glob=$lt_file_magic_glob # Find potential files using nocaseglob when deplibs_check_method = "file_magic". want_nocaseglob=$lt_want_nocaseglob # DLL creation program. DLLTOOL=$lt_DLLTOOL # Command to associate shared and link libraries. sharedlib_from_linklib_cmd=$lt_sharedlib_from_linklib_cmd # The archiver. AR=$lt_AR # Flags to create an archive. AR_FLAGS=$lt_AR_FLAGS # How to feed a file listing to the archiver. archiver_list_spec=$lt_archiver_list_spec # A symbol stripping program. STRIP=$lt_STRIP # Commands used to install an old-style archive. RANLIB=$lt_RANLIB old_postinstall_cmds=$lt_old_postinstall_cmds old_postuninstall_cmds=$lt_old_postuninstall_cmds # Whether to use a lock for old archive extraction. lock_old_archive_extraction=$lock_old_archive_extraction # A C compiler. LTCC=$lt_CC # LTCC compiler flags. LTCFLAGS=$lt_CFLAGS # Take the output of nm and produce a listing of raw symbols and C names. global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe # Transform the output of nm in a proper C declaration. global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl # Transform the output of nm into a list of symbols to manually relocate. global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import # Transform the output of nm in a C name address pair. global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address # Transform the output of nm in a C name address pair when lib prefix is needed. global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix # The name lister interface. nm_interface=$lt_lt_cv_nm_interface # Specify filename containing input files for \$NM. nm_file_list_spec=$lt_nm_file_list_spec # The root where to search for dependent libraries,and where our libraries should be installed. lt_sysroot=$lt_sysroot # Command to truncate a binary pipe. lt_truncate_bin=$lt_lt_cv_truncate_bin # The name of the directory that contains temporary libtool files. objdir=$objdir # Used to examine libraries when file_magic_cmd begins with "file". MAGIC_CMD=$MAGIC_CMD # Must we lock files when doing compilation? need_locks=$lt_need_locks # Manifest tool. MANIFEST_TOOL=$lt_MANIFEST_TOOL # Tool to manipulate archived DWARF debug symbol files on Mac OS X. DSYMUTIL=$lt_DSYMUTIL # Tool to change global to local symbols on Mac OS X. NMEDIT=$lt_NMEDIT # Tool to manipulate fat objects and archives on Mac OS X. LIPO=$lt_LIPO # ldd/readelf like tool for Mach-O binaries on Mac OS X. OTOOL=$lt_OTOOL # ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. OTOOL64=$lt_OTOOL64 # Old archive suffix (normally "a"). libext=$libext # Shared library suffix (normally ".so"). shrext_cmds=$lt_shrext_cmds # The commands to extract the exported symbol list from a shared archive. extract_expsyms_cmds=$lt_extract_expsyms_cmds # Variables whose values should be saved in libtool wrapper scripts and # restored at link time. variables_saved_for_relink=$lt_variables_saved_for_relink # Do we need the "lib" prefix for modules? need_lib_prefix=$need_lib_prefix # Do we need a version for libraries? need_version=$need_version # Library versioning type. version_type=$version_type # Shared library runtime path variable. runpath_var=$runpath_var # Shared library path variable. shlibpath_var=$shlibpath_var # Is shlibpath searched before the hard-coded library search path? shlibpath_overrides_runpath=$shlibpath_overrides_runpath # Format of library name prefix. libname_spec=$lt_libname_spec # List of archive names. First name is the real one, the rest are links. # The last name is the one that the linker finds with -lNAME library_names_spec=$lt_library_names_spec # The coded name of the library, if different from the real name. soname_spec=$lt_soname_spec # Permission mode override for installation of shared libraries. install_override_mode=$lt_install_override_mode # Command to use after installation of a shared archive. postinstall_cmds=$lt_postinstall_cmds # Command to use after uninstallation of a shared archive. postuninstall_cmds=$lt_postuninstall_cmds # Commands used to finish a libtool library installation in a directory. finish_cmds=$lt_finish_cmds # As "finish_cmds", except a single script fragment to be evaled but # not shown. finish_eval=$lt_finish_eval # Whether we should hardcode library paths into libraries. hardcode_into_libs=$hardcode_into_libs # Compile-time system search path for libraries. sys_lib_search_path_spec=$lt_sys_lib_search_path_spec # Detected run-time system search path for libraries. sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path # Explicit LT_SYS_LIBRARY_PATH set during ./configure time. configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path # Whether dlopen is supported. dlopen_support=$enable_dlopen # Whether dlopen of programs is supported. dlopen_self=$enable_dlopen_self # Whether dlopen of statically linked programs is supported. dlopen_self_static=$enable_dlopen_self_static # Commands to strip libraries. old_striplib=$lt_old_striplib striplib=$lt_striplib # The linker used to build libraries. LD=$lt_LD # How to create reloadable object files. reload_flag=$lt_reload_flag reload_cmds=$lt_reload_cmds # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds # A language specific compiler. CC=$lt_compiler # Is the compiler the GNU compiler? with_gcc=$GCC # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds archive_expsym_cmds=$lt_archive_expsym_cmds # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds module_expsym_cmds=$lt_module_expsym_cmds # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms # Symbols that must always be exported. include_expsyms=$lt_include_expsyms # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds # Specify filename containing input files. file_list_spec=$lt_file_list_spec # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects postdep_objects=$lt_postdep_objects predeps=$lt_predeps postdeps=$lt_postdeps # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE # func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x$2 in x) ;; *:) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\" ;; x:*) eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\" ;; *::*) eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\" ;; *) eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\" ;; esac } # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in $*""; do case $cc_temp in compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac ltmain=$ac_aux_dir/ltmain.sh # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" cat <<_LT_EOF >> "$ofile" # ### BEGIN LIBTOOL TAG CONFIG: CXX # The linker used to build libraries. LD=$lt_LD_CXX # How to create reloadable object files. reload_flag=$lt_reload_flag_CXX reload_cmds=$lt_reload_cmds_CXX # Commands used to build an old-style archive. old_archive_cmds=$lt_old_archive_cmds_CXX # A language specific compiler. CC=$lt_compiler_CXX # Is the compiler the GNU compiler? with_gcc=$GCC_CXX # Compiler flag to turn off builtin functions. no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX # Additional compiler flags for building library objects. pic_flag=$lt_lt_prog_compiler_pic_CXX # How to pass a linker flag through the compiler. wl=$lt_lt_prog_compiler_wl_CXX # Compiler flag to prevent dynamic linking. link_static_flag=$lt_lt_prog_compiler_static_CXX # Does compiler simultaneously support -c and -o options? compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX # Whether or not to add -lc for building shared libraries. build_libtool_need_lc=$archive_cmds_need_lc_CXX # Whether or not to disallow shared libs when runtime libs are static. allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX # Compiler flag to allow reflexive dlopens. export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX # Compiler flag to generate shared objects directly from archives. whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX # Whether the compiler copes with passing no objects directly. compiler_needs_object=$lt_compiler_needs_object_CXX # Create an old-style archive from a shared archive. old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX # Create a temporary old-style archive to link instead of a shared archive. old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX # Commands used to build a shared archive. archive_cmds=$lt_archive_cmds_CXX archive_expsym_cmds=$lt_archive_expsym_cmds_CXX # Commands used to build a loadable module if different from building # a shared archive. module_cmds=$lt_module_cmds_CXX module_expsym_cmds=$lt_module_expsym_cmds_CXX # Whether we are building with GNU ld or not. with_gnu_ld=$lt_with_gnu_ld_CXX # Flag that allows shared libraries with undefined symbols to be built. allow_undefined_flag=$lt_allow_undefined_flag_CXX # Flag that enforces no undefined symbols. no_undefined_flag=$lt_no_undefined_flag_CXX # Flag to hardcode \$libdir into a binary during linking. # This must work even if \$libdir does not exist hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX # Whether we need a single "-rpath" flag with a separated argument. hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary. hardcode_direct=$hardcode_direct_CXX # Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes # DIR into the resulting binary and the resulting library dependency is # "absolute",i.e impossible to change by setting \$shlibpath_var if the # library is relocated. hardcode_direct_absolute=$hardcode_direct_absolute_CXX # Set to "yes" if using the -LDIR flag during linking hardcodes DIR # into the resulting binary. hardcode_minus_L=$hardcode_minus_L_CXX # Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR # into the resulting binary. hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX # Set to "yes" if building a shared library automatically hardcodes DIR # into the library and all subsequent libraries and executables linked # against it. hardcode_automatic=$hardcode_automatic_CXX # Set to yes if linker adds runtime paths of dependent libraries # to runtime path list. inherit_rpath=$inherit_rpath_CXX # Whether libtool must link a program against all its dependency libraries. link_all_deplibs=$link_all_deplibs_CXX # Set to "yes" if exported symbols are required. always_export_symbols=$always_export_symbols_CXX # The commands to list exported symbols. export_symbols_cmds=$lt_export_symbols_cmds_CXX # Symbols that should not be listed in the preloaded symbols. exclude_expsyms=$lt_exclude_expsyms_CXX # Symbols that must always be exported. include_expsyms=$lt_include_expsyms_CXX # Commands necessary for linking programs (against libraries) with templates. prelink_cmds=$lt_prelink_cmds_CXX # Commands necessary for finishing linking programs. postlink_cmds=$lt_postlink_cmds_CXX # Specify filename containing input files. file_list_spec=$lt_file_list_spec_CXX # How to hardcode a shared library path into an executable. hardcode_action=$hardcode_action_CXX # The directories searched by this compiler when creating a shared library. compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX # Dependencies to place before and after the objects being linked to # create a shared library. predep_objects=$lt_predep_objects_CXX postdep_objects=$lt_postdep_objects_CXX predeps=$lt_predeps_CXX postdeps=$lt_postdeps_CXX # The library search path used internally by the compiler when linking # a shared library. compiler_lib_search_path=$lt_compiler_lib_search_path_CXX # ### END LIBTOOL TAG CONFIG: CXX _LT_EOF ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi proj-6.3.1/src/0000775000175000017500000000000013620226610010263 500000000000000proj-6.3.1/src/wkt_parser.hpp0000664000175000017500000000405113601752712013103 00000000000000/****************************************************************************** * Project: PROJ * Purpose: WKT parser common routines * Author: Even Rouault, * ****************************************************************************** * Copyright (c) 2018 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 PJ_WKT_PARSER_H_INCLUDED #define PJ_WKT_PARSER_H_INCLUDED //! @cond Doxygen_Suppress #include struct pj_wkt_parse_context { const char *pszInput = nullptr; const char *pszLastSuccess = nullptr; const char *pszNext = nullptr; std::string errorMsg{}; pj_wkt_parse_context() = default; pj_wkt_parse_context(const pj_wkt_parse_context &) = delete; pj_wkt_parse_context &operator=(const pj_wkt_parse_context &) = delete; }; void pj_wkt_error(pj_wkt_parse_context *context, const char *msg); //! @endcond #endif // PJ_WKT_PARSER_H_INCLUDED proj-6.3.1/src/wkt1_generated_parser.h0000664000175000017500000000520713601752712014646 00000000000000/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 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_PJ_WKT1_SRC_WKT1_GENERATED_PARSER_H_INCLUDED # define YY_PJ_WKT1_SRC_WKT1_GENERATED_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int pj_wkt1_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 pj_wkt1_parse (pj_wkt1_parse_context *context); #endif /* !YY_PJ_WKT1_SRC_WKT1_GENERATED_PARSER_H_INCLUDED */ proj-6.3.1/src/transform.cpp0000664000175000017500000010433113601752712012732 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Perform overall coordinate system to coordinate system * transformations (pj_transform() function) including reprojection * and datum shifting. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2000, 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 #include #include #include "proj.h" #include "proj_internal.h" #include "geocent.h" static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, long point_count, int point_offset, double *x, double *y, double *z ); #ifndef SRS_WGS84_SEMIMAJOR #define SRS_WGS84_SEMIMAJOR 6378137.0 #endif #ifndef SRS_WGS84_ESQUARED #define SRS_WGS84_ESQUARED 0.0066943799901413165 #endif #define Dx_BF (defn->datum_params[0]) #define Dy_BF (defn->datum_params[1]) #define Dz_BF (defn->datum_params[2]) #define Rx_BF (defn->datum_params[3]) #define Ry_BF (defn->datum_params[4]) #define Rz_BF (defn->datum_params[5]) #define M_BF (defn->datum_params[6]) /* ** This table is intended to indicate for any given error code ** whether that error will occur for all locations (ie. ** it is a problem with the coordinate system as a whole) in which case the ** value would be 0, or if the problem is with the point being transformed ** in which case the value is 1. ** ** At some point we might want to move this array in with the error message ** list or something, but while experimenting with it this should be fine. ** ** ** NOTE (2017-10-01): Non-transient errors really should have resulted in a ** PJ==0 during initialization, and hence should be handled at the level ** before calling pj_transform. The only obvious example of the contrary ** appears to be the PJD_ERR_GRID_AREA case, which may also be taken to ** mean "no grids available" ** ** */ static const int transient_error[70] = { /* 0 1 2 3 4 5 6 7 8 9 */ /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 50 to 59 */ 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, /* 60 to 69 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,}; /* -------------------------------------------------------------------- */ /* Read transient_error[] in a safe way. */ /* -------------------------------------------------------------------- */ static int get_transient_error_value(int pos_index) { const int array_size = (int)(sizeof(transient_error) / sizeof(transient_error[0])); if( pos_index < 0 || pos_index >= array_size ) { return 0; } return transient_error[pos_index]; } /* -------------------------------------------------------------------- */ /* Transform unusual input coordinate axis orientation to */ /* standard form if needed. */ /* -------------------------------------------------------------------- */ static int adjust_axes (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { /* Nothing to do? */ if (0==strcmp(P->axis,"enu")) return 0; return adjust_axis( P->ctx, P->axis, dir==PJ_FWD ? 1: 0, n, dist, x, y, z ); } /* ----------------------------------------------------------------------- */ /* Transform geographic (lat/long) source coordinates to */ /* cartesian ("geocentric"), if needed */ /* ----------------------------------------------------------------------- */ static int geographic_to_cartesian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { int res; long i; double fac = P->to_meter; /* Nothing to do? */ if (!P->is_geocent) return 0; if ( z == nullptr ) { pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); return PJD_ERR_GEOCENTRIC; } if (PJ_FWD==dir) { fac = P->fr_meter; res = pj_geodetic_to_geocentric( P->a_orig, P->es_orig, n, dist, x, y, z ); if (res) return res; } if (fac != 1.0) { for( i = 0; i < n; i++ ) { if( x[dist*i] != HUGE_VAL ) { x[dist*i] *= fac; y[dist*i] *= fac; z[dist*i] *= fac; } } } if (PJ_FWD==dir) return 0; return pj_geocentric_to_geodetic( P->a_orig, P->es_orig, n, dist, x, y, z ); } /* -------------------------------------------------------------------- */ /* Transform destination points to projection coordinates, if */ /* desired. */ /* */ /* Ought to fold this into projected_to_geographic */ /* -------------------------------------------------------------------- */ static int geographic_to_projected (PJ *P, long n, int dist, double *x, double *y, double *z) { long i; /* Nothing to do? */ if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) return 0; if (P->is_geocent) return 0; if(P->fwd3d != nullptr && !(z == nullptr && P->is_latlong)) { /* Three dimensions must be defined */ if ( z == nullptr) { pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); return PJD_ERR_GEOCENTRIC; } for( i = 0; i < n; i++ ) { PJ_XYZ projected_loc; PJ_LPZ geodetic_loc; geodetic_loc.lam = x[dist*i]; geodetic_loc.phi = y[dist*i]; geodetic_loc.z = z[dist*i]; if (geodetic_loc.lam == HUGE_VAL) continue; proj_errno_reset( P ); projected_loc = pj_fwd3d( geodetic_loc, P); if( P->ctx->last_errno != 0 ) { if( (P->ctx->last_errno != EDOM && P->ctx->last_errno != ERANGE) && (P->ctx->last_errno > 0 || P->ctx->last_errno < -44 || n == 1 || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) { return P->ctx->last_errno; } else { projected_loc.x = HUGE_VAL; projected_loc.y = HUGE_VAL; projected_loc.z = HUGE_VAL; } } x[dist*i] = projected_loc.x; y[dist*i] = projected_loc.y; z[dist*i] = projected_loc.z; } return 0; } // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 if( P->right == PJ_IO_UNITS_WHATEVER && P->descr && strncmp(P->descr, "General Oblique Transformation", strlen("General Oblique Transformation")) == 0 ) { P->right = PJ_IO_UNITS_PROJECTED; } for( i = 0; i ctx->last_errno != 0 ) { if( (P->ctx->last_errno != EDOM && P->ctx->last_errno != ERANGE) && (P->ctx->last_errno > 0 || P->ctx->last_errno < -44 || n == 1 || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) { return P->ctx->last_errno; } else { projected_loc.x = HUGE_VAL; projected_loc.y = HUGE_VAL; } } x[dist*i] = projected_loc.x; y[dist*i] = projected_loc.y; } return 0; } /* ----------------------------------------------------------------------- */ /* Transform projected source coordinates to lat/long, if needed */ /* ----------------------------------------------------------------------- */ static int projected_to_geographic (PJ *P, long n, int dist, double *x, double *y, double *z) { long i; /* Nothing to do? */ if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) return 0; if (P->is_geocent) return 0; /* Check first if projection is invertible. */ if( (P->inv3d == nullptr) && (P->inv == nullptr)) { pj_ctx_set_errno(pj_get_ctx(P), PJD_ERR_NON_CONV_INV_MERI_DIST); pj_log( pj_get_ctx(P), PJ_LOG_ERROR, "pj_transform(): source projection not invertable" ); return PJD_ERR_NON_CONV_INV_MERI_DIST; } /* If invertible - First try inv3d if defined */ if (P->inv3d != nullptr && !(z == nullptr && P->is_latlong)) { /* Three dimensions must be defined */ if ( z == nullptr) { pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); return PJD_ERR_GEOCENTRIC; } for (i=0; i < n; i++) { PJ_XYZ projected_loc; PJ_LPZ geodetic_loc; projected_loc.x = x[dist*i]; projected_loc.y = y[dist*i]; projected_loc.z = z[dist*i]; if (projected_loc.x == HUGE_VAL) continue; proj_errno_reset( P ); geodetic_loc = pj_inv3d(projected_loc, P); if( P->ctx->last_errno != 0 ) { if( (P->ctx->last_errno != EDOM && P->ctx->last_errno != ERANGE) && (P->ctx->last_errno > 0 || P->ctx->last_errno < -44 || n == 1 || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) { return P->ctx->last_errno; } else { geodetic_loc.lam = HUGE_VAL; geodetic_loc.phi = HUGE_VAL; geodetic_loc.z = HUGE_VAL; } } x[dist*i] = geodetic_loc.lam; y[dist*i] = geodetic_loc.phi; z[dist*i] = geodetic_loc.z; } return 0; } // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782 if( P->right == PJ_IO_UNITS_WHATEVER && P->descr && strncmp(P->descr, "General Oblique Transformation", strlen("General Oblique Transformation")) == 0 ) { P->right = PJ_IO_UNITS_PROJECTED; } /* Fallback to the original PROJ.4 API 2d inversion - inv */ for( i = 0; i < n; i++ ) { PJ_XY projected_loc; PJ_LP geodetic_loc; projected_loc.x = x[dist*i]; projected_loc.y = y[dist*i]; if( projected_loc.x == HUGE_VAL ) continue; proj_errno_reset( P ); geodetic_loc = pj_inv( projected_loc, P ); if( P->ctx->last_errno != 0 ) { if( (P->ctx->last_errno != EDOM && P->ctx->last_errno != ERANGE) && (P->ctx->last_errno > 0 || P->ctx->last_errno < -44 || n == 1 || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) { return P->ctx->last_errno; } else { geodetic_loc.lam = HUGE_VAL; geodetic_loc.phi = HUGE_VAL; } } x[dist*i] = geodetic_loc.lam; y[dist*i] = geodetic_loc.phi; } return 0; } /* -------------------------------------------------------------------- */ /* Adjust for the prime meridian if needed. */ /* -------------------------------------------------------------------- */ static int prime_meridian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x) { int i; double pm = P->from_greenwich; /* Nothing to do? */ if (pm==0.0) return 0; if (!(P->is_geocent || P->is_latlong)) return 0; if (dir==PJ_FWD) pm = -pm; for (i = 0; i < n; i++) if (x[dist*i] != HUGE_VAL) x[dist*i] += pm; return 0; } /* -------------------------------------------------------------------- */ /* Adjust for vertical scale factor if needed */ /* -------------------------------------------------------------------- */ static int height_unit (PJ *P, PJ_DIRECTION dir, long n, int dist, double *z) { int i; double fac = P->vto_meter; if (PJ_FWD==dir) fac = P->vfr_meter; /* Nothing to do? */ if (fac==1.0) return 0; if (nullptr==z) return 0; if (P->is_latlong) return 0; /* done in pj_inv3d() / pj_fwd3d() */ for (i = 0; i < n; i++) if (z[dist*i] != HUGE_VAL ) z[dist*i] *= fac; return 0; } /* -------------------------------------------------------------------- */ /* Transform to ellipsoidal heights if needed */ /* -------------------------------------------------------------------- */ static int geometric_to_orthometric (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { int err; if (0==P->has_geoid_vgrids) return 0; if (z==nullptr) return PJD_ERR_GEOCENTRIC; err = pj_apply_vgridshift (P, "sgeoidgrids", &(P->vgridlist_geoid), &(P->vgridlist_geoid_count), dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); if (err) return pj_ctx_get_errno(P->ctx); return 0; } /* -------------------------------------------------------------------- */ /* Convert datums if needed, and possible. */ /* -------------------------------------------------------------------- */ static int datum_transform (PJ *P, PJ *Q, long n, int dist, double *x, double *y, double *z) { if (0==pj_datum_transform (P, Q, n, dist, x, y, z)) return 0; if (P->ctx->last_errno) return P->ctx->last_errno; return Q->ctx->last_errno; } /* -------------------------------------------------------------------- */ /* If a wrapping center other than 0 is provided, rewrap around */ /* the suggested center (for latlong coordinate systems only). */ /* -------------------------------------------------------------------- */ static int long_wrap (PJ *P, long n, int dist, double *x) { long i; /* Nothing to do? */ if (P->is_geocent) return 0; if (!P->is_long_wrap_set) return 0; if (!P->is_latlong) return 0; for (i = 0; i < n; i++ ) { double val = x[dist*i]; if (val == HUGE_VAL) continue; /* Get fast in ] -2 PI, 2 PI [ range */ val = fmod(val, M_TWOPI); while( val < P->long_wrap_center - M_PI ) val += M_TWOPI; while( val > P->long_wrap_center + M_PI ) val -= M_TWOPI; x[dist*i] = val; } return 0; } /************************************************************************/ /* pj_transform() */ /* */ /* Currently this function doesn't recognise if two projections */ /* are identical (to short circuit reprojection) because it is */ /* difficult to compare PJ structures (since there are some */ /* projection specific components). */ /************************************************************************/ int pj_transform( PJ *src, PJ *dst, long point_count, int point_offset, double *x, double *y, double *z ){ int err; src->ctx->last_errno = 0; dst->ctx->last_errno = 0; if( point_offset == 0 ) point_offset = 1; /* Bring input to "normal form": longitude, latitude, ellipsoidal height */ err = adjust_axes (src, PJ_INV, point_count, point_offset, x, y, z); if (err) return err; err = geographic_to_cartesian (src, PJ_INV, point_count, point_offset, x, y, z); if (err) return err; err = projected_to_geographic (src, point_count, point_offset, x, y, z); if (err) return err; err = prime_meridian (src, PJ_INV, point_count, point_offset, x); if (err) return err; err = height_unit (src, PJ_INV, point_count, point_offset, z); if (err) return err; err = geometric_to_orthometric (src, PJ_INV, point_count, point_offset, x, y, z); if (err) return err; /* At the center of the process we do the datum shift (if needed) */ err = datum_transform(src, dst, point_count, point_offset, x, y, z ); if (err) return err; /* Now get out on the other side: Bring "normal form" to output form */ err = geometric_to_orthometric (dst, PJ_FWD, point_count, point_offset, x, y, z); if (err) return err; err = height_unit (dst, PJ_FWD, point_count, point_offset, z); if (err) return err; err = prime_meridian (dst, PJ_FWD, point_count, point_offset, x); if (err) return err; err = geographic_to_cartesian (dst, PJ_FWD, point_count, point_offset, x, y, z); if (err) return err; err = geographic_to_projected (dst, point_count, point_offset, x, y, z); if (err) return err; err = long_wrap (dst, point_count, point_offset, x); if (err) return err; err = adjust_axes (dst, PJ_FWD, point_count, point_offset, x, y, z); if (err) return err; return 0; } /************************************************************************/ /* pj_geodetic_to_geocentric() */ /************************************************************************/ int pj_geodetic_to_geocentric( double a, double es, long point_count, int point_offset, double *x, double *y, double *z ) { double b; int i; GeocentricInfo gi; int ret_errno = 0; if( es == 0.0 ) b = a; else b = a * sqrt(1-es); if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) { return PJD_ERR_GEOCENTRIC; } for( i = 0; i < point_count; i++ ) { long io = i * point_offset; if( x[io] == HUGE_VAL ) continue; if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], x+io, y+io, z+io ) != 0 ) { ret_errno = PJD_ERR_LAT_OR_LON_EXCEED_LIMIT; x[io] = y[io] = HUGE_VAL; /* but keep processing points! */ } } return ret_errno; } /************************************************************************/ /* pj_geocentric_to_geodetic() */ /************************************************************************/ int pj_geocentric_to_geodetic( double a, double es, long point_count, int point_offset, double *x, double *y, double *z ) { double b; int i; GeocentricInfo gi; if( es == 0.0 ) b = a; else b = a * sqrt(1-es); if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) { return PJD_ERR_GEOCENTRIC; } for( i = 0; i < point_count; i++ ) { long io = i * point_offset; if( x[io] == HUGE_VAL ) continue; pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], y+io, x+io, z+io ); } return 0; } /************************************************************************/ /* pj_compare_datums() */ /* */ /* Returns TRUE if the two datums are identical, otherwise */ /* FALSE. */ /************************************************************************/ int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) { if( srcdefn->datum_type != dstdefn->datum_type ) { return 0; } else if( srcdefn->a_orig != dstdefn->a_orig || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 ) { /* the tolerance for es is to ensure that GRS80 and WGS84 are considered identical */ return 0; } else if( srcdefn->datum_type == PJD_3PARAM ) { return (srcdefn->datum_params[0] == dstdefn->datum_params[0] && srcdefn->datum_params[1] == dstdefn->datum_params[1] && srcdefn->datum_params[2] == dstdefn->datum_params[2]); } else if( srcdefn->datum_type == PJD_7PARAM ) { return (srcdefn->datum_params[0] == dstdefn->datum_params[0] && srcdefn->datum_params[1] == dstdefn->datum_params[1] && srcdefn->datum_params[2] == dstdefn->datum_params[2] && srcdefn->datum_params[3] == dstdefn->datum_params[3] && srcdefn->datum_params[4] == dstdefn->datum_params[4] && srcdefn->datum_params[5] == dstdefn->datum_params[5] && srcdefn->datum_params[6] == dstdefn->datum_params[6]); } else if( srcdefn->datum_type == PJD_GRIDSHIFT ) { const char* srcnadgrids = pj_param(srcdefn->ctx, srcdefn->params,"snadgrids").s; const char* dstnadgrids = pj_param(dstdefn->ctx, dstdefn->params,"snadgrids").s; return srcnadgrids != nullptr && dstnadgrids != nullptr && strcmp( srcnadgrids, dstnadgrids ) == 0; } else return 1; } /************************************************************************/ /* pj_geocentic_to_wgs84() */ /************************************************************************/ static int pj_geocentric_to_wgs84( PJ *defn, long point_count, int point_offset, double *x, double *y, double *z ) { int i; if( defn->datum_type == PJD_3PARAM ) { for( i = 0; i < point_count; i++ ) { long io = i * point_offset; if( x[io] == HUGE_VAL ) continue; x[io] = x[io] + Dx_BF; y[io] = y[io] + Dy_BF; z[io] = z[io] + Dz_BF; } } else if( defn->datum_type == PJD_7PARAM ) { for( i = 0; i < point_count; i++ ) { long io = i * point_offset; double x_out, y_out, z_out; if( x[io] == HUGE_VAL ) continue; x_out = M_BF*( x[io] - Rz_BF*y[io] + Ry_BF*z[io]) + Dx_BF; y_out = M_BF*( Rz_BF*x[io] + y[io] - Rx_BF*z[io]) + Dy_BF; z_out = M_BF*(-Ry_BF*x[io] + Rx_BF*y[io] + z[io]) + Dz_BF; x[io] = x_out; y[io] = y_out; z[io] = z_out; } } return 0; } /************************************************************************/ /* pj_geocentic_from_wgs84() */ /************************************************************************/ static int pj_geocentric_from_wgs84( PJ *defn, long point_count, int point_offset, double *x, double *y, double *z ) { int i; if( defn->datum_type == PJD_3PARAM ) { for( i = 0; i < point_count; i++ ) { long io = i * point_offset; if( x[io] == HUGE_VAL ) continue; x[io] = x[io] - Dx_BF; y[io] = y[io] - Dy_BF; z[io] = z[io] - Dz_BF; } } else if( defn->datum_type == PJD_7PARAM ) { for( i = 0; i < point_count; i++ ) { long io = i * point_offset; double x_tmp, y_tmp, z_tmp; if( x[io] == HUGE_VAL ) continue; x_tmp = (x[io] - Dx_BF) / M_BF; y_tmp = (y[io] - Dy_BF) / M_BF; z_tmp = (z[io] - Dz_BF) / M_BF; x[io] = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp; y[io] = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp; z[io] = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp; } } return 0; } /************************************************************************/ /* pj_datum_transform() */ /* */ /* The input should be long/lat/z coordinates in radians in the */ /* source datum, and the output should be long/lat/z */ /* coordinates in radians in the destination datum. */ /************************************************************************/ int pj_datum_transform( PJ *src, PJ *dst, long point_count, int point_offset, double *x, double *y, double *z ) { double src_a, src_es, dst_a, dst_es; int z_is_temp = FALSE; /* -------------------------------------------------------------------- */ /* We cannot do any meaningful datum transformation if either */ /* the source or destination are of an unknown datum type */ /* (ie. only a +ellps declaration, no +datum). This is new */ /* behavior for PROJ 4.6.0. */ /* -------------------------------------------------------------------- */ if( src->datum_type == PJD_UNKNOWN || dst->datum_type == PJD_UNKNOWN ) return 0; /* -------------------------------------------------------------------- */ /* Short cut if the datums are identical. */ /* -------------------------------------------------------------------- */ if( pj_compare_datums( src, dst ) ) return 0; src_a = src->a_orig; src_es = src->es_orig; dst_a = dst->a_orig; dst_es = dst->es_orig; /* -------------------------------------------------------------------- */ /* Create a temporary Z array if one is not provided. */ /* -------------------------------------------------------------------- */ if( z == nullptr ) { size_t bytes = sizeof(double) * point_count * point_offset; z = (double *) pj_malloc(bytes); memset( z, 0, bytes ); z_is_temp = TRUE; } #define CHECK_RETURN(defn) {if( defn->ctx->last_errno != 0 && (defn->ctx->last_errno > 0 || get_transient_error_value(-defn->ctx->last_errno) == 0) ) { if( z_is_temp ) pj_dalloc(z); return defn->ctx->last_errno; }} /* -------------------------------------------------------------------- */ /* If this datum requires grid shifts, then apply it to geodetic */ /* coordinates. */ /* -------------------------------------------------------------------- */ if( src->datum_type == PJD_GRIDSHIFT ) { pj_apply_gridshift_2( src, 0, point_count, point_offset, x, y, z ); CHECK_RETURN(src); src_a = SRS_WGS84_SEMIMAJOR; src_es = SRS_WGS84_ESQUARED; } if( dst->datum_type == PJD_GRIDSHIFT ) { dst_a = SRS_WGS84_SEMIMAJOR; dst_es = SRS_WGS84_ESQUARED; } /* ==================================================================== */ /* Do we need to go through geocentric coordinates? */ /* ==================================================================== */ if( src_es != dst_es || src_a != dst_a || src->datum_type == PJD_3PARAM || src->datum_type == PJD_7PARAM || dst->datum_type == PJD_3PARAM || dst->datum_type == PJD_7PARAM) { /* -------------------------------------------------------------------- */ /* Convert to geocentric coordinates. */ /* -------------------------------------------------------------------- */ src->ctx->last_errno = pj_geodetic_to_geocentric( src_a, src_es, point_count, point_offset, x, y, z ); CHECK_RETURN(src); /* -------------------------------------------------------------------- */ /* Convert between datums. */ /* -------------------------------------------------------------------- */ if( src->datum_type == PJD_3PARAM || src->datum_type == PJD_7PARAM ) { pj_geocentric_to_wgs84( src, point_count, point_offset,x,y,z); CHECK_RETURN(src); } if( dst->datum_type == PJD_3PARAM || dst->datum_type == PJD_7PARAM ) { pj_geocentric_from_wgs84( dst, point_count,point_offset,x,y,z); CHECK_RETURN(dst); } /* -------------------------------------------------------------------- */ /* Convert back to geodetic coordinates. */ /* -------------------------------------------------------------------- */ dst->ctx->last_errno = pj_geocentric_to_geodetic( dst_a, dst_es, point_count, point_offset, x, y, z ); CHECK_RETURN(dst); } /* -------------------------------------------------------------------- */ /* Apply grid shift to destination if required. */ /* -------------------------------------------------------------------- */ if( dst->datum_type == PJD_GRIDSHIFT ) { pj_apply_gridshift_2( dst, 1, point_count, point_offset, x, y, z ); CHECK_RETURN(dst); } if( z_is_temp ) pj_dalloc( z ); return 0; } /************************************************************************/ /* adjust_axis() */ /* */ /* Normalize or de-normalized the x/y/z axes. The normal form */ /* is "enu" (easting, northing, up). */ /************************************************************************/ static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, long point_count, int point_offset, double *x, double *y, double *z ) { double x_in, y_in, z_in = 0.0; int i, i_axis; if( !denormalize_flag ) { for( i = 0; i < point_count; i++ ) { x_in = x[point_offset*i]; y_in = y[point_offset*i]; if( z ) z_in = z[point_offset*i]; for( i_axis = 0; i_axis < 3; i_axis++ ) { double value; if( i_axis == 0 ) value = x_in; else if( i_axis == 1 ) value = y_in; else value = z_in; switch( axis[i_axis] ) { case 'e': x[point_offset*i] = value; break; case 'w': x[point_offset*i] = -value; break; case 'n': y[point_offset*i] = value; break; case 's': y[point_offset*i] = -value; break; case 'u': if( z ) z[point_offset*i] = value; break; case 'd': if( z ) z[point_offset*i] = -value; break; default: pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); return PJD_ERR_AXIS; } } /* i_axis */ } /* i (point) */ } else /* denormalize */ { for( i = 0; i < point_count; i++ ) { x_in = x[point_offset*i]; y_in = y[point_offset*i]; if( z ) z_in = z[point_offset*i]; for( i_axis = 0; i_axis < 3; i_axis++ ) { double *target; if( i_axis == 2 && z == nullptr ) continue; if( i_axis == 0 ) target = x; else if( i_axis == 1 ) target = y; else target = z; switch( axis[i_axis] ) { case 'e': target[point_offset*i] = x_in; break; case 'w': target[point_offset*i] = -x_in; break; case 'n': target[point_offset*i] = y_in; break; case 's': target[point_offset*i] = -y_in; break; case 'u': target[point_offset*i] = z_in; break; case 'd': target[point_offset*i] = -z_in; break; default: pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); return PJD_ERR_AXIS; } } /* i_axis */ } /* i (point) */ } return 0; } proj-6.3.1/src/wkt2_generated_parser.h0000664000175000017500000001272413601752712014651 00000000000000/* A Bison parser, made by GNU Bison 3.0.4. */ /* Bison interface for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 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_PJ_WKT2_SRC_WKT2_GENERATED_PARSER_H_INCLUDED # define YY_PJ_WKT2_SRC_WKT2_GENERATED_PARSER_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int pj_wkt2_debug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { END = 0, T_PROJECTION = 258, T_DATUM = 259, T_SPHEROID = 260, T_PRIMEM = 261, T_UNIT = 262, T_AXIS = 263, T_PARAMETER = 264, T_GEODCRS = 265, T_LENGTHUNIT = 266, T_ANGLEUNIT = 267, T_SCALEUNIT = 268, T_TIMEUNIT = 269, T_ELLIPSOID = 270, T_CS = 271, T_ID = 272, T_PROJCRS = 273, T_BASEGEODCRS = 274, T_MERIDIAN = 275, T_BEARING = 276, T_ORDER = 277, T_ANCHOR = 278, T_CONVERSION = 279, T_METHOD = 280, T_REMARK = 281, T_GEOGCRS = 282, T_BASEGEOGCRS = 283, T_SCOPE = 284, T_AREA = 285, T_BBOX = 286, T_CITATION = 287, T_URI = 288, T_VERTCRS = 289, T_VDATUM = 290, T_GEOIDMODEL = 291, T_COMPOUNDCRS = 292, T_PARAMETERFILE = 293, T_COORDINATEOPERATION = 294, T_SOURCECRS = 295, T_TARGETCRS = 296, T_INTERPOLATIONCRS = 297, T_OPERATIONACCURACY = 298, T_CONCATENATEDOPERATION = 299, T_STEP = 300, T_BOUNDCRS = 301, T_ABRIDGEDTRANSFORMATION = 302, T_DERIVINGCONVERSION = 303, T_TDATUM = 304, T_CALENDAR = 305, T_TIMEORIGIN = 306, T_TIMECRS = 307, T_VERTICALEXTENT = 308, T_TIMEEXTENT = 309, T_USAGE = 310, T_DYNAMIC = 311, T_FRAMEEPOCH = 312, T_MODEL = 313, T_VELOCITYGRID = 314, T_ENSEMBLE = 315, T_MEMBER = 316, T_ENSEMBLEACCURACY = 317, T_DERIVEDPROJCRS = 318, T_BASEPROJCRS = 319, T_EDATUM = 320, T_ENGCRS = 321, T_PDATUM = 322, T_PARAMETRICCRS = 323, T_PARAMETRICUNIT = 324, T_BASEVERTCRS = 325, T_BASEENGCRS = 326, T_BASEPARAMCRS = 327, T_BASETIMECRS = 328, T_EPOCH = 329, T_COORDEPOCH = 330, T_COORDINATEMETADATA = 331, T_POINTMOTIONOPERATION = 332, T_VERSION = 333, T_GEODETICCRS = 334, T_GEODETICDATUM = 335, T_PROJECTEDCRS = 336, T_PRIMEMERIDIAN = 337, T_GEOGRAPHICCRS = 338, T_TRF = 339, T_VERTICALCRS = 340, T_VERTICALDATUM = 341, T_VRF = 342, T_TIMEDATUM = 343, T_TEMPORALQUANTITY = 344, T_ENGINEERINGDATUM = 345, T_ENGINEERINGCRS = 346, T_PARAMETRICDATUM = 347, T_AFFINE = 348, T_CARTESIAN = 349, T_CYLINDRICAL = 350, T_ELLIPSOIDAL = 351, T_LINEAR = 352, T_PARAMETRIC = 353, T_POLAR = 354, T_SPHERICAL = 355, T_VERTICAL = 356, T_TEMPORAL = 357, T_TEMPORALCOUNT = 358, T_TEMPORALMEASURE = 359, T_ORDINAL = 360, T_TEMPORALDATETIME = 361, T_NORTH = 362, T_NORTHNORTHEAST = 363, T_NORTHEAST = 364, T_EASTNORTHEAST = 365, T_EAST = 366, T_EASTSOUTHEAST = 367, T_SOUTHEAST = 368, T_SOUTHSOUTHEAST = 369, T_SOUTH = 370, T_SOUTHSOUTHWEST = 371, T_SOUTHWEST = 372, T_WESTSOUTHWEST = 373, T_WEST = 374, T_WESTNORTHWEST = 375, T_NORTHWEST = 376, T_NORTHNORTHWEST = 377, T_UP = 378, T_DOWN = 379, T_GEOCENTRICX = 380, T_GEOCENTRICY = 381, T_GEOCENTRICZ = 382, T_COLUMNPOSITIVE = 383, T_COLUMNNEGATIVE = 384, T_ROWPOSITIVE = 385, T_ROWNEGATIVE = 386, T_DISPLAYRIGHT = 387, T_DISPLAYLEFT = 388, T_DISPLAYUP = 389, T_DISPLAYDOWN = 390, T_FORWARD = 391, T_AFT = 392, T_PORT = 393, T_STARBOARD = 394, T_CLOCKWISE = 395, T_COUNTERCLOCKWISE = 396, T_TOWARDS = 397, T_AWAYFROM = 398, T_FUTURE = 399, T_PAST = 400, T_UNSPECIFIED = 401, T_STRING = 402, T_UNSIGNED_INTEGER_DIFFERENT_ONE_TWO_THREE = 403 }; #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 pj_wkt2_parse (pj_wkt2_parse_context *context); #endif /* !YY_PJ_WKT2_SRC_WKT2_GENERATED_PARSER_H_INCLUDED */ proj-6.3.1/src/proj_constants.h0000664000175000017500000006057213617003642013440 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: Constants * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 PROJ_CONSTANTS_INCLUDED #define PROJ_CONSTANTS_INCLUDED /* Projection methods */ #define EPSG_NAME_METHOD_TRANSVERSE_MERCATOR "Transverse Mercator" #define EPSG_CODE_METHOD_TRANSVERSE_MERCATOR 9807 #define EPSG_NAME_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED \ "Transverse Mercator (South Orientated)" #define EPSG_CODE_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED 9808 #define PROJ_WKT2_NAME_METHOD_TWO_POINT_EQUIDISTANT "Two Point Equidistant" #define EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP \ "Lambert Conic Conformal (1SP)" #define EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP 9801 #define EPSG_NAME_METHOD_NZMG "New Zealand Map Grid" #define EPSG_CODE_METHOD_NZMG 9811 #define EPSG_NAME_METHOD_TUNISIA_MAPPING_GRID "Tunisia Mapping Grid" #define EPSG_CODE_METHOD_TUNISIA_MAPPING_GRID 9816 #define EPSG_NAME_METHOD_ALBERS_EQUAL_AREA "Albers Equal Area" #define EPSG_CODE_METHOD_ALBERS_EQUAL_AREA 9822 #define EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP \ "Lambert Conic Conformal (2SP)" #define EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP 9802 #define EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_BELGIUM \ "Lambert Conic Conformal (2SP Belgium)" #define EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_BELGIUM 9803 #define EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN \ "Lambert Conic Conformal (2SP Michigan)" #define EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN 1051 #define EPSG_NAME_METHOD_MODIFIED_AZIMUTHAL_EQUIDISTANT \ "Modified Azimuthal Equidistant" #define EPSG_CODE_METHOD_MODIFIED_AZIMUTHAL_EQUIDISTANT 9832 #define EPSG_NAME_METHOD_GUAM_PROJECTION "Guam Projection" #define EPSG_CODE_METHOD_GUAM_PROJECTION 9831 #define EPSG_NAME_METHOD_BONNE "Bonne" #define EPSG_CODE_METHOD_BONNE 9827 #define EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL \ "Lambert Cylindrical Equal Area (Spherical)" #define EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL 9834 #define EPSG_NAME_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA \ "Lambert Cylindrical Equal Area" #define EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA 9835 #define EPSG_NAME_METHOD_CASSINI_SOLDNER "Cassini-Soldner" #define EPSG_CODE_METHOD_CASSINI_SOLDNER 9806 #define PROJ_WKT2_NAME_METHOD_EQUIDISTANT_CONIC "Equidistant Conic" #define PROJ_WKT2_NAME_METHOD_ECKERT_I "Eckert I" #define PROJ_WKT2_NAME_METHOD_ECKERT_II "Eckert II" #define PROJ_WKT2_NAME_METHOD_ECKERT_III "Eckert III" #define PROJ_WKT2_NAME_METHOD_ECKERT_IV "Eckert IV" #define PROJ_WKT2_NAME_METHOD_ECKERT_V "Eckert V" #define PROJ_WKT2_NAME_METHOD_ECKERT_VI "Eckert VI" #define EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL "Equidistant Cylindrical" #define EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL 1028 #define EPSG_NAME_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL \ "Equidistant Cylindrical (Spherical)" #define EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL 1029 #define PROJ_WKT2_NAME_METHOD_GALL_STEREOGRAPHIC "Gall Stereographic" #define PROJ_WKT2_NAME_METHOD_GOODE_HOMOLOSINE "Goode Homolosine" #define PROJ_WKT2_NAME_METHOD_INTERRUPTED_GOODE_HOMOLOSINE \ "Interrupted Goode Homolosine" #define PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X \ "Geostationary Satellite (Sweep X)" #define PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_Y \ "Geostationary Satellite (Sweep Y)" #define PROJ_WKT2_NAME_METHOD_GAUSS_SCHREIBER_TRANSVERSE_MERCATOR \ "Gauss Schreiber Transverse Mercator" #define PROJ_WKT2_NAME_METHOD_GNOMONIC "Gnomonic" #define EPSG_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A \ "Hotine Oblique Mercator (variant A)" #define EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A 9812 #define EPSG_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B \ "Hotine Oblique Mercator (variant B)" #define EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B 9815 #define PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN \ "Hotine Oblique Mercator Two Point Natural Origin" #define PROJ_WKT2_NAME_INTERNATIONAL_MAP_WORLD_POLYCONIC \ "International Map of the World Polyconic" #define EPSG_NAME_METHOD_KROVAK_NORTH_ORIENTED "Krovak (North Orientated)" #define EPSG_CODE_METHOD_KROVAK_NORTH_ORIENTED 1041 #define EPSG_NAME_METHOD_KROVAK "Krovak" #define EPSG_CODE_METHOD_KROVAK 9819 #define EPSG_NAME_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA \ "Lambert Azimuthal Equal Area" #define EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA 9820 #define EPSG_NAME_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL \ "Lambert Azimuthal Equal Area (Spherical)" #define EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL 1027 #define PROJ_WKT2_NAME_METHOD_MILLER_CYLINDRICAL "Miller Cylindrical" #define EPSG_CODE_METHOD_MERCATOR_VARIANT_A 9804 #define EPSG_NAME_METHOD_MERCATOR_VARIANT_A "Mercator (variant A)" #define EPSG_CODE_METHOD_MERCATOR_VARIANT_B 9805 #define EPSG_NAME_METHOD_MERCATOR_VARIANT_B "Mercator (variant B)" #define EPSG_NAME_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR \ "Popular Visualisation Pseudo Mercator" #define EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR 1024 #define PROJ_WKT2_NAME_METHOD_MOLLWEIDE "Mollweide" #define EPSG_NAME_METHOD_OBLIQUE_STEREOGRAPHIC "Oblique Stereographic" #define EPSG_CODE_METHOD_OBLIQUE_STEREOGRAPHIC 9809 #define EPSG_NAME_METHOD_ORTHOGRAPHIC "Orthographic" #define EPSG_CODE_METHOD_ORTHOGRAPHIC 9840 #define EPSG_NAME_METHOD_AMERICAN_POLYCONIC "American Polyconic" #define EPSG_CODE_METHOD_AMERICAN_POLYCONIC 9818 #define EPSG_NAME_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A \ "Polar Stereographic (variant A)" #define EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A 9810 #define EPSG_NAME_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B \ "Polar Stereographic (variant B)" #define EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B 9829 #define PROJ_WKT2_NAME_METHOD_ROBINSON "Robinson" #define PROJ_WKT2_NAME_METHOD_SINUSOIDAL "Sinusoidal" #define PROJ_WKT2_NAME_METHOD_STEREOGRAPHIC "Stereographic" #define PROJ_WKT2_NAME_METHOD_VAN_DER_GRINTEN "Van Der Grinten" #define PROJ_WKT2_NAME_METHOD_WAGNER_I "Wagner I" #define PROJ_WKT2_NAME_METHOD_WAGNER_II "Wagner II" #define PROJ_WKT2_NAME_METHOD_WAGNER_III "Wagner III" #define PROJ_WKT2_NAME_METHOD_WAGNER_IV "Wagner IV" #define PROJ_WKT2_NAME_METHOD_WAGNER_V "Wagner V" #define PROJ_WKT2_NAME_METHOD_WAGNER_VI "Wagner VI" #define PROJ_WKT2_NAME_METHOD_WAGNER_VII "Wagner VII" #define PROJ_WKT2_NAME_METHOD_QUADRILATERALIZED_SPHERICAL_CUBE \ "Quadrilateralized Spherical Cube" #define PROJ_WKT2_NAME_METHOD_SPHERICAL_CROSS_TRACK_HEIGHT \ "Spherical Cross-Track Height" #define EPSG_NAME_METHOD_EQUAL_EARTH "Equal Earth" #define EPSG_CODE_METHOD_EQUAL_EARTH 1078 #define EPSG_NAME_METHOD_LABORDE_OBLIQUE_MERCATOR "Laborde Oblique Mercator" #define EPSG_CODE_METHOD_LABORDE_OBLIQUE_MERCATOR 9813 #define EPSG_NAME_METHOD_VERTICAL_PERSPECTIVE "Vertical Perspective" #define EPSG_CODE_METHOD_VERTICAL_PERSPECTIVE 9838 #define PROJ_WKT2_NAME_METHOD_POLE_ROTATION_GRIB_CONVENTION "Pole rotation (GRIB convention)" /* ------------------------------------------------------------------------ */ /* Projection parameters */ #define EPSG_NAME_PARAMETER_COLATITUDE_CONE_AXIS "Co-latitude of cone axis" #define EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS 1036 #define EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN \ "Latitude of natural origin" #define EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN 8801 #define EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN \ "Longitude of natural origin" #define EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN 8802 #define EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN \ "Scale factor at natural origin" #define EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN 8805 #define EPSG_NAME_PARAMETER_FALSE_EASTING "False easting" #define EPSG_CODE_PARAMETER_FALSE_EASTING 8806 #define EPSG_NAME_PARAMETER_FALSE_NORTHING "False northing" #define EPSG_CODE_PARAMETER_FALSE_NORTHING 8807 #define EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE \ "Latitude of projection centre" #define EPSG_CODE_PARAMETER_LATITUDE_PROJECTION_CENTRE 8811 #define EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE \ "Longitude of projection centre" #define EPSG_CODE_PARAMETER_LONGITUDE_PROJECTION_CENTRE 8812 #define EPSG_NAME_PARAMETER_AZIMUTH_INITIAL_LINE "Azimuth of initial line" #define EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE 8813 #define EPSG_NAME_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID \ "Angle from Rectified to Skew Grid" #define EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID 8814 #define EPSG_NAME_PARAMETER_SCALE_FACTOR_INITIAL_LINE \ "Scale factor on initial line" #define EPSG_CODE_PARAMETER_SCALE_FACTOR_INITIAL_LINE 8815 #define EPSG_NAME_PARAMETER_EASTING_PROJECTION_CENTRE \ "Easting at projection centre" #define EPSG_CODE_PARAMETER_EASTING_PROJECTION_CENTRE 8816 #define EPSG_NAME_PARAMETER_NORTHING_PROJECTION_CENTRE \ "Northing at projection centre" #define EPSG_CODE_PARAMETER_NORTHING_PROJECTION_CENTRE 8817 #define EPSG_NAME_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL \ "Latitude of pseudo standard parallel" #define EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL 8818 #define EPSG_NAME_PARAMETER_SCALE_FACTOR_PSEUDO_STANDARD_PARALLEL \ "Scale factor on pseudo standard parallel" #define EPSG_CODE_PARAMETER_SCALE_FACTOR_PSEUDO_STANDARD_PARALLEL 8819 #define EPSG_NAME_PARAMETER_LATITUDE_FALSE_ORIGIN "Latitude of false origin" #define EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN 8821 #define EPSG_NAME_PARAMETER_LONGITUDE_FALSE_ORIGIN "Longitude of false origin" #define EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN 8822 #define EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL \ "Latitude of 1st standard parallel" #define EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL 8823 #define EPSG_NAME_PARAMETER_LATITUDE_2ND_STD_PARALLEL \ "Latitude of 2nd standard parallel" #define EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL 8824 #define EPSG_NAME_PARAMETER_EASTING_FALSE_ORIGIN "Easting at false origin" #define EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN 8826 #define EPSG_NAME_PARAMETER_NORTHING_FALSE_ORIGIN "Northing at false origin" #define EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN 8827 #define EPSG_NAME_PARAMETER_LATITUDE_STD_PARALLEL "Latitude of standard parallel" #define EPSG_CODE_PARAMETER_LATITUDE_STD_PARALLEL 8832 #define EPSG_NAME_PARAMETER_LONGITUDE_OF_ORIGIN "Longitude of origin" #define EPSG_CODE_PARAMETER_LONGITUDE_OF_ORIGIN 8833 #define EPSG_NAME_PARAMETER_ELLIPSOID_SCALE_FACTOR "Ellipsoid scaling factor" #define EPSG_CODE_PARAMETER_ELLIPSOID_SCALE_FACTOR 1038 #define EPSG_NAME_PARAMETER_LATITUDE_TOPOGRAPHIC_ORIGIN "Latitude of topocentric origin" #define EPSG_CODE_PARAMETER_LATITUDE_TOPOGRAPHIC_ORIGIN 8834 #define EPSG_NAME_PARAMETER_LONGITUDE_TOPOGRAPHIC_ORIGIN "Longitude of topocentric origin" #define EPSG_CODE_PARAMETER_LONGITUDE_TOPOGRAPHIC_ORIGIN 8835 #define EPSG_NAME_PARAMETER_ELLIPSOIDAL_HEIGHT_TOPOCENTRIC_ORIGIN "Ellipsoidal height of topocentric origin" #define EPSG_CODE_PARAMETER_ELLIPSOIDAL_HEIGHT_TOPOCENTRIC_ORIGIN 8836 #define EPSG_NAME_PARAMETER_VIEWPOINT_HEIGHT "Viewpoint height" #define EPSG_CODE_PARAMETER_VIEWPOINT_HEIGHT 8840 /* ------------------------------------------------------------------------ */ /* Other conversions and transformations */ #define EPSG_NAME_METHOD_COORDINATE_FRAME_GEOCENTRIC \ "Coordinate Frame rotation (geocentric domain)" #define EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC 1032 #define EPSG_NAME_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D \ "Coordinate Frame rotation (geog2D domain)" #define EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D 9607 #define EPSG_NAME_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D \ "Coordinate Frame rotation (geog3D domain)" #define EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D 1038 #define EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC \ "Position Vector transformation (geocentric domain)" #define EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC 1033 #define EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D \ "Position Vector transformation (geog2D domain)" #define EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D 9606 #define EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D \ "Position Vector transformation (geog3D domain)" #define EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D 1037 #define EPSG_NAME_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC \ "Geocentric translations (geocentric domain)" #define EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC 1031 #define EPSG_NAME_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D \ "Geocentric translations (geog2D domain)" #define EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D 9603 #define EPSG_NAME_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D \ "Geocentric translations (geog3D domain)" #define EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D 1035 #define EPSG_NAME_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC \ "Time-dependent Position Vector tfm (geocentric)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC 1053 #define EPSG_NAME_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D \ "Time-dependent Position Vector tfm (geog2D)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D 1054 #define EPSG_NAME_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D \ "Time-dependent Position Vector tfm (geog3D)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D 1055 #define EPSG_NAME_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC \ "Time-dependent Coordinate Frame rotation geocen)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC \ 1056 #define EPSG_NAME_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D \ "Time-dependent Coordinate Frame rotation (geog2D)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D 1057 #define EPSG_NAME_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D \ "Time-dependent Coordinate Frame rotation (geog3D)" #define EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D 1058 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_CF_GEOCENTRIC \ "Molodensky-Badekas (CF geocentric domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOCENTRIC 1034 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_PV_GEOCENTRIC \ "Molodensky-Badekas (PV geocentric domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOCENTRIC 1061 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_3D \ "Molodensky-Badekas (CF geog3D domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_3D 1039 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_3D \ "Molodensky-Badekas (PV geog3D domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_3D 1062 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_2D \ "Molodensky-Badekas (CF geog2D domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_2D 9636 #define EPSG_NAME_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_2D \ "Molodensky-Badekas (PV geog2D domain)" #define EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_2D 1063 #define EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION 8605 #define EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION 8606 #define EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION 8607 #define EPSG_CODE_PARAMETER_X_AXIS_ROTATION 8608 #define EPSG_CODE_PARAMETER_Y_AXIS_ROTATION 8609 #define EPSG_CODE_PARAMETER_Z_AXIS_ROTATION 8610 #define EPSG_CODE_PARAMETER_SCALE_DIFFERENCE 8611 #define EPSG_CODE_PARAMETER_RATE_X_AXIS_TRANSLATION 1040 #define EPSG_CODE_PARAMETER_RATE_Y_AXIS_TRANSLATION 1041 #define EPSG_CODE_PARAMETER_RATE_Z_AXIS_TRANSLATION 1042 #define EPSG_CODE_PARAMETER_RATE_X_AXIS_ROTATION 1043 #define EPSG_CODE_PARAMETER_RATE_Y_AXIS_ROTATION 1044 #define EPSG_CODE_PARAMETER_RATE_Z_AXIS_ROTATION 1045 #define EPSG_CODE_PARAMETER_RATE_SCALE_DIFFERENCE 1046 #define EPSG_CODE_PARAMETER_REFERENCE_EPOCH 1047 #define EPSG_CODE_PARAMETER_TRANSFORMATION_REFERENCE_EPOCH 1049 #define EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION "X-axis translation" #define EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION "Y-axis translation" #define EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION "Z-axis translation" #define EPSG_NAME_PARAMETER_X_AXIS_ROTATION "X-axis rotation" #define EPSG_NAME_PARAMETER_Y_AXIS_ROTATION "Y-axis rotation" #define EPSG_NAME_PARAMETER_Z_AXIS_ROTATION "Z-axis rotation" #define EPSG_NAME_PARAMETER_SCALE_DIFFERENCE "Scale difference" #define EPSG_NAME_PARAMETER_RATE_X_AXIS_TRANSLATION \ "Rate of change of X-axis translation" #define EPSG_NAME_PARAMETER_RATE_Y_AXIS_TRANSLATION \ "Rate of change of Y-axis translation" #define EPSG_NAME_PARAMETER_RATE_Z_AXIS_TRANSLATION \ "Rate of change of Z-axis translation" #define EPSG_NAME_PARAMETER_RATE_X_AXIS_ROTATION \ "Rate of change of X-axis rotation" #define EPSG_NAME_PARAMETER_RATE_Y_AXIS_ROTATION \ "Rate of change of Y-axis rotation" #define EPSG_NAME_PARAMETER_RATE_Z_AXIS_ROTATION \ "Rate of change of Z-axis rotation" #define EPSG_NAME_PARAMETER_RATE_SCALE_DIFFERENCE \ "Rate of change of Scale difference" #define EPSG_NAME_PARAMETER_REFERENCE_EPOCH "Parameter reference epoch" #define EPSG_CODE_PARAMETER_ORDINATE_1_EVAL_POINT 8617 #define EPSG_CODE_PARAMETER_ORDINATE_2_EVAL_POINT 8618 #define EPSG_CODE_PARAMETER_ORDINATE_3_EVAL_POINT 8667 #define \ EPSG_NAME_PARAMETER_ORDINATE_1_EVAL_POINT "Ordinate 1 of evaluation point" #define \ EPSG_NAME_PARAMETER_ORDINATE_2_EVAL_POINT "Ordinate 2 of evaluation point" #define \ EPSG_NAME_PARAMETER_ORDINATE_3_EVAL_POINT "Ordinate 3 of evaluation point" #define EPSG_NAME_PARAMETER_TRANSFORMATION_REFERENCE_EPOCH \ "Transformation reference epoch" #define EPSG_NAME_METHOD_MOLODENSKY "Molodensky" #define EPSG_CODE_METHOD_MOLODENSKY 9604 #define EPSG_NAME_METHOD_ABRIDGED_MOLODENSKY "Abridged Molodensky" #define EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY 9605 #define EPSG_CODE_PARAMETER_SEMI_MAJOR_AXIS_DIFFERENCE 8654 #define EPSG_CODE_PARAMETER_FLATTENING_DIFFERENCE 8655 #define EPSG_NAME_PARAMETER_SEMI_MAJOR_AXIS_DIFFERENCE \ "Semi-major axis length difference" #define \ EPSG_NAME_PARAMETER_FLATTENING_DIFFERENCE "Flattening difference" #define PROJ_WKT2_NAME_PARAMETER_SOUTH_POLE_LATITUDE_GRIB_CONVENTION \ "Latitude of the southern pole (GRIB convention)" #define PROJ_WKT2_NAME_PARAMETER_SOUTH_POLE_LONGITUDE_GRIB_CONVENTION \ "Longitude of the southern pole (GRIB convention)" #define PROJ_WKT2_NAME_PARAMETER_AXIS_ROTATION_GRIB_CONVENTION \ "Axis rotation (GRIB convention)" /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_NTV1 9614 #define EPSG_NAME_METHOD_NTV1 "NTv1" #define EPSG_CODE_METHOD_NTV2 9615 #define EPSG_NAME_METHOD_NTV2 "NTv2" #define EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE 8656 #define EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE \ "Latitude and longitude difference file" #define EPSG_NAME_PARAMETER_GEOID_CORRECTION_FILENAME \ "Geoid (height correction) model file" #define EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME 8666 /* ------------------------------------------------------------------------ */ #define PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D \ "GravityRelatedHeight to Geographic3D" #define PROJ_WKT2_NAME_METHOD_CTABLE2 "CTABLE2" /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_VERTCON 9658 #define EPSG_NAME_METHOD_VERTCON_OLDNAME "VERTCON" #define EPSG_NAME_METHOD_VERTCON "Vertical Offset by Grid Interpolation (VERTCON)" #define EPSG_CODE_METHOD_VERTICALGRID_NZLVD 1071 #define EPSG_NAME_METHOD_VERTICALGRID_NZLVD "Vertical Offset by Grid Interpolation (NZLVD)" #define EPSG_CODE_METHOD_VERTICALGRID_GTX 1084 #define EPSG_NAME_METHOD_VERTICALGRID_GTX "Vertical Offset by Grid Interpolation (gtx)" #define EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE "Vertical offset file" #define EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE 8732 /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_NADCON 9613 #define EPSG_NAME_METHOD_NADCON "NADCON" #define EPSG_NAME_PARAMETER_LATITUDE_DIFFERENCE_FILE "Latitude difference file" #define EPSG_CODE_PARAMETER_LATITUDE_DIFFERENCE_FILE 8657 #define \ EPSG_NAME_PARAMETER_LONGITUDE_DIFFERENCE_FILE "Longitude difference file" #define EPSG_CODE_PARAMETER_LONGITUDE_DIFFERENCE_FILE 8658 /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT 1069 #define EPSG_NAME_METHOD_CHANGE_VERTICAL_UNIT "Change of Vertical Unit" #define EPSG_NAME_PARAMETER_UNIT_CONVERSION_SCALAR "Unit conversion scalar" #define EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR 1051 /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_LONGITUDE_ROTATION 9601 #define EPSG_NAME_METHOD_LONGITUDE_ROTATION "Longitude rotation" #define EPSG_CODE_METHOD_VERTICAL_OFFSET 9616 #define EPSG_NAME_METHOD_VERTICAL_OFFSET "Vertical Offset" #define EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS 9619 #define EPSG_NAME_METHOD_GEOGRAPHIC2D_OFFSETS "Geographic2D offsets" #define EPSG_CODE_METHOD_GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS 9618 #define EPSG_NAME_METHOD_GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS \ "Geographic2D with Height Offsets" #define EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS 9660 #define EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSETS "Geographic3D offsets" #define EPSG_CODE_METHOD_GEOGRAPHIC_GEOCENTRIC 9602 #define \ EPSG_NAME_METHOD_GEOGRAPHIC_GEOCENTRIC "Geographic/geocentric conversions" #define EPSG_NAME_PARAMETER_LATITUDE_OFFSET "Latitude offset" #define EPSG_CODE_PARAMETER_LATITUDE_OFFSET 8601 #define EPSG_NAME_PARAMETER_LONGITUDE_OFFSET "Longitude offset" #define EPSG_CODE_PARAMETER_LONGITUDE_OFFSET 8602 #define EPSG_NAME_PARAMETER_VERTICAL_OFFSET "Vertical Offset" #define EPSG_CODE_PARAMETER_VERTICAL_OFFSET 8603 #define EPSG_NAME_PARAMETER_GEOID_UNDULATION "Geoid undulation" #define EPSG_CODE_PARAMETER_GEOID_UNDULATION 8604 /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_AFFINE_PARAMETRIC_TRANSFORMATION 9624 #define EPSG_NAME_METHOD_AFFINE_PARAMETRIC_TRANSFORMATION \ "Affine parametric transformation" #define EPSG_NAME_PARAMETER_A0 "A0" #define EPSG_CODE_PARAMETER_A0 8623 #define EPSG_NAME_PARAMETER_A1 "A1" #define EPSG_CODE_PARAMETER_A1 8624 #define EPSG_NAME_PARAMETER_A2 "A2" #define EPSG_CODE_PARAMETER_A2 8625 #define EPSG_NAME_PARAMETER_B0 "B0" #define EPSG_CODE_PARAMETER_B0 8639 #define EPSG_NAME_PARAMETER_B1 "B1" #define EPSG_CODE_PARAMETER_B1 8640 #define EPSG_NAME_PARAMETER_B2 "B2" #define EPSG_CODE_PARAMETER_B2 8641 /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_2D 9843 #define EPSG_NAME_METHOD_AXIS_ORDER_REVERSAL_2D "Axis Order Reversal (2D)" #define EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_3D 9844 #define EPSG_NAME_METHOD_AXIS_ORDER_REVERSAL_3D \ "Axis Order Reversal (Geographic3D horizontal)" /* ------------------------------------------------------------------------ */ #define EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL 1068 #define EPSG_NAME_METHOD_HEIGHT_DEPTH_REVERSAL "Height Depth Reversal" #endif /* PROJ_CONSTANTS_INCLUDED */ proj-6.3.1/src/ctx.cpp0000664000175000017500000002273413601752712011523 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Implementation of the projCtx thread context object. * 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 #include #include #include #include "proj_experimental.h" #include "proj_internal.h" /************************************************************************/ /* pj_get_ctx() */ /************************************************************************/ projCtx pj_get_ctx( projPJ pj ) { if (nullptr==pj) return pj_get_default_ctx (); if (nullptr==pj->ctx) return pj_get_default_ctx (); return pj->ctx; } /************************************************************************/ /* pj_set_ctx() */ /* */ /* Note we do not deallocate the old context! */ /************************************************************************/ void pj_set_ctx( projPJ pj, projCtx ctx ) { if (pj==nullptr) return; pj->ctx = ctx; if( pj->is_pipeline ) { pj_pipeline_assign_context_to_steps(pj, ctx); } for( const auto &alt: pj->alternativeCoordinateOperations ) { pj_set_ctx(alt.pj, ctx); } } /************************************************************************/ /* proj_assign_context() */ /************************************************************************/ /** \brief Re-assign a context to a PJ* object. * * This may be useful if the PJ* has been created with a context that is * thread-specific, and is later used in another thread. In that case, * the user may want to assign another thread-specific context to the * object. */ void proj_assign_context( PJ* pj, PJ_CONTEXT* ctx ) { pj_set_ctx( pj, ctx ); } /************************************************************************/ /* createDefault() */ /************************************************************************/ projCtx_t projCtx_t::createDefault() { projCtx_t ctx; ctx.debug_level = PJ_LOG_NONE; ctx.logger = pj_stderr_logger; ctx.fileapi = pj_get_default_fileapi(); if( getenv("PROJ_DEBUG") != nullptr ) { if( atoi(getenv("PROJ_DEBUG")) >= -PJ_LOG_DEBUG_MINOR ) ctx.debug_level = atoi(getenv("PROJ_DEBUG")); else ctx.debug_level = PJ_LOG_DEBUG_MINOR; } return ctx; } /************************************************************************/ /* set_search_paths() */ /************************************************************************/ void projCtx_t::set_search_paths(const std::vector& search_paths_in ) { search_paths = search_paths_in; delete[] c_compat_paths; c_compat_paths = nullptr; if( !search_paths.empty() ) { c_compat_paths = new const char*[search_paths.size()]; for( size_t i = 0; i < search_paths.size(); ++i ) { c_compat_paths[i] = search_paths[i].c_str(); } } } /************************************************************************/ /* projCtx_t(const projCtx_t& other) */ /************************************************************************/ projCtx_t::projCtx_t(const projCtx_t& other) { debug_level = other.debug_level; logger = other.logger; logger_app_data = other.logger_app_data; fileapi = other.fileapi; epsg_file_exists = other.epsg_file_exists; set_search_paths(other.search_paths); file_finder = other.file_finder; file_finder_legacy = other.file_finder_legacy; file_finder_user_data = other.file_finder_user_data; } /************************************************************************/ /* pj_get_default_ctx() */ /************************************************************************/ projCtx pj_get_default_ctx() { // C++11 rules guarantee a thread-safe instantiation. static projCtx_t default_context(projCtx_t::createDefault()); return &default_context; } /************************************************************************/ /* ~projCtx_t() */ /************************************************************************/ projCtx_t::~projCtx_t() { delete[] c_compat_paths; proj_context_delete_cpp_context(cpp_context); } /************************************************************************/ /* pj_ctx_alloc() */ /************************************************************************/ projCtx pj_ctx_alloc() { return new (std::nothrow) projCtx_t(*pj_get_default_ctx()); } /************************************************************************/ /* pj_ctx_free() */ /************************************************************************/ void pj_ctx_free( projCtx ctx ) { delete ctx; } /************************************************************************/ /* pj_ctx_get_errno() */ /************************************************************************/ int pj_ctx_get_errno( projCtx ctx ) { if (nullptr==ctx) return pj_get_default_ctx ()->last_errno; return ctx->last_errno; } /************************************************************************/ /* pj_ctx_set_errno() */ /* */ /* Also sets the global errno */ /************************************************************************/ void pj_ctx_set_errno( projCtx ctx, int new_errno ) { ctx->last_errno = new_errno; if( new_errno == 0 ) return; errno = new_errno; pj_errno = new_errno; } /************************************************************************/ /* pj_ctx_set_debug() */ /************************************************************************/ void pj_ctx_set_debug( projCtx ctx, int new_debug ) { if (nullptr==ctx) return; ctx->debug_level = new_debug; } /************************************************************************/ /* pj_ctx_set_logger() */ /************************************************************************/ void pj_ctx_set_logger( projCtx ctx, void (*new_logger)(void*,int,const char*) ) { if (nullptr==ctx) return; ctx->logger = new_logger; } /************************************************************************/ /* pj_ctx_set_app_data() */ /************************************************************************/ void pj_ctx_set_app_data( projCtx ctx, void *new_app_data ) { if (nullptr==ctx) return; ctx->logger_app_data = new_app_data; } /************************************************************************/ /* pj_ctx_get_app_data() */ /************************************************************************/ void *pj_ctx_get_app_data( projCtx ctx ) { if (nullptr==ctx) return nullptr; return ctx->logger_app_data; } /************************************************************************/ /* pj_ctx_set_fileapi() */ /************************************************************************/ void pj_ctx_set_fileapi( projCtx ctx, projFileAPI *fileapi ) { if (nullptr==ctx) return; ctx->fileapi = fileapi; } /************************************************************************/ /* pj_ctx_get_fileapi() */ /************************************************************************/ projFileAPI *pj_ctx_get_fileapi( projCtx ctx ) { if (nullptr==ctx) return nullptr; return ctx->fileapi; } proj-6.3.1/src/adjlon.cpp0000664000175000017500000000075213601752712012170 00000000000000/* reduce argument to range +/- PI */ #include #include "proj.h" #include "proj_internal.h" double adjlon (double lon) { /* Let lon slightly overshoot, to avoid spurious sign switching at the date line */ if (fabs (lon) < M_PI + 1e-12) return lon; /* adjust to 0..2pi range */ lon += M_PI; /* remove integral # of 'revolutions'*/ lon -= M_TWOPI * floor(lon / M_TWOPI); /* adjust back to -pi..pi range */ lon -= M_PI; return lon; } proj-6.3.1/src/geocent.h0000664000175000017500000001421313601752712012007 00000000000000#ifndef GEOCENT_H #define GEOCENT_H /***************************************************************************/ /* RSC IDENTIFIER: GEOCENTRIC * * ABSTRACT * * This component provides conversions between Geodetic coordinates (latitude, * longitude in radians and height in meters) and Geocentric coordinates * (X, Y, Z) in meters. * * ERROR HANDLING * * This component checks parameters for valid values. If an invalid value * is found, the error code is combined with the current error code using * the bitwise or. This combining allows multiple error codes to be * returned. The possible error codes are: * * GEOCENT_NO_ERROR : No errors occurred in function * GEOCENT_LAT_ERROR : Latitude out of valid range * (-90 to 90 degrees) * GEOCENT_LON_ERROR : Longitude out of valid range * (-180 to 360 degrees) * GEOCENT_A_ERROR : Semi-major axis less than or equal to zero * GEOCENT_B_ERROR : Semi-minor axis less than or equal to zero * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis * * * REUSE NOTES * * GEOCENTRIC is intended for reuse by any application that performs * coordinate conversions between geodetic coordinates and geocentric * coordinates. * * * REFERENCES * * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion, * Ralph Toms, February 1996 UCRL-JC-123138. * * Further information on GEOCENTRIC can be found in the Reuse Manual. * * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center * Geospatial Information Division * 7701 Telegraph Road * Alexandria, VA 22310-3864 * * LICENSES * * None apply to this component. * * RESTRICTIONS * * GEOCENTRIC has no restrictions. * * ENVIRONMENT * * GEOCENTRIC was tested and certified in the following environments: * * 1. Solaris 2.5 with GCC version 2.8.1 * 2. Windows 95 with MS Visual C++ version 6 * * MODIFICATIONS * * Date Description * ---- ----------- * * */ /***************************************************************************/ /* * DEFINES */ #define GEOCENT_NO_ERROR 0x0000 #define GEOCENT_LAT_ERROR 0x0001 #define GEOCENT_LON_ERROR 0x0002 #define GEOCENT_A_ERROR 0x0004 #define GEOCENT_B_ERROR 0x0008 #define GEOCENT_A_LESS_B_ERROR 0x0010 /***************************************************************************/ /* * FUNCTION PROTOTYPES */ /* ensure proper linkage to c++ programs */ #ifdef __cplusplus extern "C" { #endif typedef struct { double Geocent_a; /* Semi-major axis of ellipsoid in meters */ double Geocent_b; /* Semi-minor axis of ellipsoid */ double Geocent_a2; /* Square of semi-major axis */ double Geocent_b2; /* Square of semi-minor axis */ double Geocent_e2; /* Eccentricity squared */ double Geocent_ep2; /* 2nd eccentricity squared */ } GeocentricInfo; void pj_Init_Geocentric( GeocentricInfo *gi ); long pj_Set_Geocentric_Parameters( GeocentricInfo *gi, double a, double b); /* * The function Set_Geocentric_Parameters receives the ellipsoid parameters * as inputs and sets the corresponding state variables. * * a : Semi-major axis, in meters. (input) * b : Semi-minor axis, in meters. (input) */ void pj_Get_Geocentric_Parameters ( GeocentricInfo *gi, double *a, double *b); /* * The function Get_Geocentric_Parameters returns the ellipsoid parameters * to be used in geocentric coordinate conversions. * * a : Semi-major axis, in meters. (output) * b : Semi-minor axis, in meters. (output) */ long pj_Convert_Geodetic_To_Geocentric ( GeocentricInfo *gi, double Latitude, double Longitude, double Height, double *X, double *Y, double *Z); /* * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z), * according to the current ellipsoid parameters. * * Latitude : Geodetic latitude in radians (input) * Longitude : Geodetic longitude in radians (input) * Height : Geodetic height, in meters (input) * X : Calculated Geocentric X coordinate, in meters. (output) * Y : Calculated Geocentric Y coordinate, in meters. (output) * Z : Calculated Geocentric Z coordinate, in meters. (output) * */ void pj_Convert_Geocentric_To_Geodetic (GeocentricInfo *gi, double X, double Y, double Z, double *Latitude, double *Longitude, double *Height); /* * The function Convert_Geocentric_To_Geodetic converts geocentric * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude, * and height), according to the current ellipsoid parameters. * * X : Geocentric X coordinate, in meters. (input) * Y : Geocentric Y coordinate, in meters. (input) * Z : Geocentric Z coordinate, in meters. (input) * Latitude : Calculated latitude value in radians. (output) * Longitude : Calculated longitude value in radians. (output) * Height : Calculated height value, in meters. (output) */ #ifdef __cplusplus } #endif #endif /* GEOCENT_H */ proj-6.3.1/src/param.cpp0000664000175000017500000001620413601752712012020 00000000000000/* put parameters in linked list and retrieve */ #include #include #include #include #include #include "proj.h" #include "proj_internal.h" static void unquote_string(char* param_str) { size_t len = strlen(param_str); // Remove leading and terminating spaces after equal sign const char* equal = strstr(param_str, "=\""); if( equal && equal - param_str + 1 > 2 && param_str[len-1] == '"' ) { size_t dst = equal + 1 - param_str; size_t src = dst + 1; for( ; param_str[src]; dst++, src++) { if( param_str[src] == '"' ) { if( param_str[src+1] == '"' ) { src++; } else { break; } } param_str[dst] = param_str[src]; } param_str[dst] = '\0'; } } /* create parameter list entry */ paralist *pj_mkparam(const char *str) { paralist *newitem; if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != nullptr) { newitem->used = 0; newitem->next = nullptr; if (*str == '+') ++str; (void)strcpy(newitem->param, str); unquote_string(newitem->param); } return newitem; } /* As pj_mkparam, but payload ends at first whitespace, rather than at end of */ paralist *pj_mkparam_ws (const char *str, const char **next_str) { paralist *newitem; size_t len = 0; if (nullptr==str) return nullptr; /* Find start and length of string */ while (isspace (*str)) str++; if (*str == '+') str++; bool in_string = false; for( ; str[len] != '\0'; len++ ) { if( in_string ) { if( str[len] == '"' && str[len+1] == '"' ) { len++; } else if( str[len] == '"' ) { in_string = false; } } else if( str[len] == '=' && str[len+1] == '"' ) { in_string = true; } else if( isspace(str[len]) ) { break; } } if( next_str ) *next_str = str + len; /* Use calloc to automagically 0-terminate the copy */ newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len + 1); if (nullptr==newitem) return nullptr; memmove(newitem->param, str, len); unquote_string(newitem->param); newitem->used = 0; newitem->next = nullptr; return newitem; } /**************************************************************************************/ paralist *pj_param_exists (paralist *list, const char *parameter) { /*************************************************************************************** Determine whether a given parameter exists in a paralist. If it does, return a pointer to the corresponding list element - otherwise return 0. In support of the pipeline syntax, the search is terminated once a "+step" list element is reached, in which case a 0 is returned, unless the parameter searched for is actually "step", in which case a pointer to the "step" list element is returned. This function is equivalent to the pj_param (...) call with the "opt" argument set to the parameter name preceeeded by a 't'. But by using this one, one avoids writing the code allocating memory for a new copy of parameter name, and prepending the t (for compile time known names, this is obviously not an issue). ***************************************************************************************/ paralist *next = list; const char *c = strchr (parameter, '='); size_t len = strlen (parameter); if (c) len = c - parameter; if (list==nullptr) return nullptr; for (next = list; next; next = next->next) { if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) { next->used = 1; return next; } if (0==strcmp (parameter, "step")) return nullptr; } return nullptr; } /************************************************************************/ /* pj_param() */ /* */ /* Test for presence or get parameter value. The first */ /* character in `opt' is a parameter type which can take the */ /* values: */ /* */ /* `t' - test for presence, return TRUE/FALSE in PROJVALUE.i */ /* `i' - integer value returned in PROJVALUE.i */ /* `d' - simple valued real input returned in PROJVALUE.f */ /* `r' - degrees (DMS translation applied), returned as */ /* radians in PROJVALUE.f */ /* `s' - string returned in PROJVALUE.s */ /* `b' - test for t/T/f/F, return in PROJVALUE.i */ /* */ /* Search is terminated when "step" is found, in which case */ /* 0 is returned, unless "step" was the target searched for. */ /* */ /************************************************************************/ PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) { int type; unsigned l; PROJVALUE value = {0}; if ( ctx == nullptr ) ctx = pj_get_default_ctx(); type = *opt++; if (nullptr==strchr ("tbirds", type)) { fprintf(stderr, "invalid request to pj_param, fatal\n"); exit(1); } pl = pj_param_exists (pl, opt); if (type == 't') { value.i = pl != nullptr; return value; } /* Not found */ if (nullptr==pl) { /* Return value after the switch, so that the return path is */ /* taken in all cases */ switch (type) { case 'b': case 'i': value.i = 0; break; case 'd': case 'r': value.f = 0.; break; case 's': value.s = nullptr; break; } return value; } /* Found parameter - now find its value */ pl->used |= 1; l = (int) strlen(opt); opt = pl->param + l; if (*opt == '=') ++opt; switch (type) { case 'i': /* integer input */ value.i = atoi(opt); break; case 'd': /* simple real input */ value.f = pj_atof(opt); break; case 'r': /* degrees input */ value.f = dmstor_ctx(ctx, opt, nullptr); break; case 's': /* char string */ value.s = (char *) opt; break; case 'b': /* boolean */ switch (*opt) { case 'F': case 'f': value.i = 0; break; case '\0': case 'T': case 't': value.i = 1; break; default: pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM); value.i = 0; break; } break; } return value; } proj-6.3.1/src/gridlist.cpp0000664000175000017500000001750613601752712012547 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Code to manage the list of currently loaded (cached) PJ_GRIDINFOs * See pj_gridinfo.c for details of loading individual grids. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2000, 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. *****************************************************************************/ #define PJ_LIB__ #include #include #include #include "proj.h" #include "proj_internal.h" static PJ_GRIDINFO *grid_list = nullptr; #define PJ_MAX_PATH_LENGTH 1024 /************************************************************************/ /* pj_deallocate_grids() */ /* */ /* Deallocate all loaded grids. */ /************************************************************************/ void pj_deallocate_grids() { while( grid_list != nullptr ) { PJ_GRIDINFO *item = grid_list; grid_list = grid_list->next; item->next = nullptr; pj_gridinfo_free( pj_get_default_ctx(), item ); } } /************************************************************************/ /* pj_gridlist_merge_grid() */ /* */ /* Find/load the named gridfile and merge it into the */ /* last_nadgrids_list. */ /************************************************************************/ static int pj_gridlist_merge_gridfile( projCtx ctx, const char *gridname, PJ_GRIDINFO ***p_gridlist, int *p_gridcount, int *p_gridmax ) { int got_match=0; PJ_GRIDINFO *this_grid, *tail = nullptr; /* -------------------------------------------------------------------- */ /* Try to find in the existing list of loaded grids. Add all */ /* matching grids as with NTv2 we can get many grids from one */ /* file (one shared gridname). */ /* -------------------------------------------------------------------- */ for( this_grid = grid_list; this_grid != nullptr; this_grid = this_grid->next) { if( strcmp(this_grid->gridname,gridname) == 0 ) { got_match = 1; /* don't add to the list if it is invalid. */ if( this_grid->ct == nullptr ) return 0; /* do we need to grow the list? */ if( *p_gridcount >= *p_gridmax - 2 ) { PJ_GRIDINFO **new_list; int new_max = *p_gridmax + 20; new_list = (PJ_GRIDINFO **) pj_calloc(new_max, sizeof(void *)); if (!new_list) { pj_ctx_set_errno( ctx, ENOMEM ); return 0; } if( *p_gridlist != nullptr ) { memcpy( new_list, *p_gridlist, sizeof(void *) * (*p_gridmax) ); pj_dalloc( *p_gridlist ); } *p_gridlist = new_list; *p_gridmax = new_max; } /* add to the list */ (*p_gridlist)[(*p_gridcount)++] = this_grid; (*p_gridlist)[*p_gridcount] = nullptr; } tail = this_grid; } if( got_match ) return 1; /* -------------------------------------------------------------------- */ /* Try to load the named grid. */ /* -------------------------------------------------------------------- */ this_grid = pj_gridinfo_init( ctx, gridname ); if( this_grid == nullptr ) { return 0; } if( tail != nullptr ) tail->next = this_grid; else grid_list = this_grid; /* -------------------------------------------------------------------- */ /* Recurse to add the grid now that it is loaded. */ /* -------------------------------------------------------------------- */ return pj_gridlist_merge_gridfile( ctx, gridname, p_gridlist, p_gridcount, p_gridmax ); } /************************************************************************/ /* pj_gridlist_from_nadgrids() */ /* */ /* This functions loads the list of grids corresponding to a */ /* particular nadgrids string into a list, and returns it. The */ /* list is kept around till a request is made with a different */ /* string in order to cut down on the string parsing cost, and */ /* the cost of building the list of tables each time. */ /************************************************************************/ PJ_GRIDINFO **pj_gridlist_from_nadgrids( projCtx ctx, const char *nadgrids, int *grid_count) { const char *s; PJ_GRIDINFO **gridlist = nullptr; int grid_max = 0; pj_errno = 0; *grid_count = 0; pj_acquire_lock(); /* -------------------------------------------------------------------- */ /* Loop processing names out of nadgrids one at a time. */ /* -------------------------------------------------------------------- */ for( s = nadgrids; *s != '\0'; ) { size_t end_char; int required = 1; char name[PJ_MAX_PATH_LENGTH]; if( *s == '@' ) { required = 0; s++; } for( end_char = 0; s[end_char] != '\0' && s[end_char] != ','; end_char++ ) {} if( end_char >= sizeof(name) ) { pj_dalloc( gridlist ); pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); pj_release_lock(); return nullptr; } strncpy( name, s, end_char ); name[end_char] = '\0'; s += end_char; if( *s == ',' ) s++; if( !pj_gridlist_merge_gridfile( ctx, name, &gridlist, grid_count, &grid_max) && required ) { pj_dalloc( gridlist ); pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); pj_release_lock(); return nullptr; } else pj_errno = 0; } pj_release_lock(); return gridlist; } proj-6.3.1/src/wkt1_grammar.y0000664000175000017500000001715113601752712013004 00000000000000%{ /****************************************************************************** * Project: PROJ * Purpose: WKT1 parser grammar * Author: Even Rouault, * ****************************************************************************** * Copyright (c) 2013-2018 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 "wkt1_parser.h" %} %define api.pure /* if the next %define is commented out, Bison 2.4 should be sufficient */ /* but will produce less prettier error messages */ %define parse.error verbose %require "3.0" %parse-param {pj_wkt1_parse_context *context} %lex-param {pj_wkt1_parse_context *context} %token T_PARAM_MT "PARAM_MT" %token T_CONCAT_MT "CONCAT_MT" %token T_INVERSE_MT "INVERSE_MT" %token T_PASSTHROUGH_MT "PASSTHROUGH_MT" %token T_PROJCS "PROJCS" %token T_PROJECTION "PROJECTION" %token T_GEOGCS "GEOGCS" %token T_DATUM "DATUM" %token T_SPHEROID "SPHEROID" %token T_PRIMEM "PRIMEM" %token T_UNIT "UNIT" %token T_GEOCCS "GEOCCS" %token T_AUTHORITY "AUTHORITY" %token T_VERT_CS "VERT_CS" %token T_VERT_DATUM "VERT_DATUM" %token T_COMPD_CS "COMPD_CS" %token T_AXIS "AXIS" %token T_TOWGS84 "TOWGS84" %token T_FITTED_CS "FITTED_CS" %token T_LOCAL_CS "LOCAL_CS" %token T_LOCAL_DATUM "LOCAL_DATUM" %token T_PARAMETER "PARAMETER" %token T_EXTENSION "EXTENSION" %token T_STRING "string" %token T_NUMBER "number" %token T_IDENTIFIER "identifier" %token END 0 "end of string" %% input: coordinate_system /* Derived from BNF grammar in OGC 01-009 OpenGIS Implementation */ /* Coordinate Transformation Services Revision 1.00 */ /* with the following additions : */ /* - accept an EXTENSION node at the end of GEOGCS, GEOCCS, PROJCS, COMPD_CS, VERT_DATUM */ /* - accept 3 parameters in TOWGS84 */ /* - accept LOCAL_CS["foo"] */ /* 7.1 Math Transform WKT */ begin_node: '[' | '(' begin_node_name: begin_node T_STRING end_node: ']' | ')' math_transform: param_mt | concat_mt | inv_mt | passthrough_mt param_mt: T_PARAM_MT begin_node_name opt_parameter_list end_node parameter: T_PARAMETER begin_node_name ',' T_NUMBER end_node opt_parameter_list: | ',' parameter opt_parameter_list concat_mt: T_CONCAT_MT begin_node math_transform opt_math_transform_list end_node opt_math_transform_list: | ',' math_transform opt_math_transform_list inv_mt: T_INVERSE_MT begin_node math_transform end_node passthrough_mt: T_PASSTHROUGH_MT begin_node integer ',' math_transform end_node /* FIXME */ integer: T_NUMBER /* 7.2 Coordinate System WKT */ coordinate_system: horz_cs | geocentric_cs | vert_cs | compd_cs | fitted_cs | local_cs horz_cs: geographic_cs | projected_cs /* opt_extension is an extension of the CT spec */ projected_cs: T_PROJCS begin_node_name ',' geographic_cs ',' projection ',' opt_parameter_list_linear_unit opt_twin_axis_extension_authority end_node opt_parameter_list_linear_unit: linear_unit | parameter_list_linear_unit parameter_list_linear_unit: parameter ',' parameter_list_linear_unit | parameter ',' linear_unit opt_twin_axis_extension_authority: | ',' twin_axis opt_extension_authority | ',' extension opt_authority | ',' authority opt_authority: | ',' authority extension: T_EXTENSION begin_node_name ',' T_STRING end_node projection: T_PROJECTION begin_node_name opt_authority end_node geographic_cs: T_GEOGCS begin_node_name',' datum ',' prime_meridian ',' angular_unit opt_twin_axis_extension_authority end_node datum: T_DATUM begin_node_name ',' spheroid opt_towgs84_authority_extension end_node opt_towgs84_authority_extension: | ',' towgs84 opt_extension_authority | ',' extension opt_authority | ',' authority spheroid: T_SPHEROID begin_node_name ',' semi_major_axis ',' inverse_flattening opt_authority end_node semi_major_axis: T_NUMBER inverse_flattening: T_NUMBER prime_meridian: T_PRIMEM begin_node_name ',' longitude opt_authority end_node longitude: T_NUMBER angular_unit: unit linear_unit: unit unit: T_UNIT begin_node_name ',' conversion_factor opt_authority end_node conversion_factor: T_NUMBER geocentric_cs: T_GEOCCS begin_node_name ',' datum ',' prime_meridian ',' linear_unit opt_three_axis_extension_authority end_node opt_three_axis_extension_authority: | ',' three_axis opt_extension_authority | ',' extension opt_authority | ',' authority three_axis: axis ',' axis ',' axis authority: T_AUTHORITY begin_node_name ',' T_STRING end_node vert_cs: T_VERT_CS begin_node_name ',' vert_datum ',' linear_unit opt_axis_authority end_node opt_axis_authority: | ',' axis opt_authority | ',' authority vert_datum: T_VERT_DATUM begin_node_name ',' datum_type opt_extension_authority end_node opt_extension_authority: | ',' extension opt_authority | ',' authority datum_type: T_NUMBER compd_cs: T_COMPD_CS begin_node_name ',' head_cs ',' tail_cs opt_extension_authority end_node head_cs: coordinate_system tail_cs: coordinate_system twin_axis: axis ',' axis axis: T_AXIS begin_node_name ',' T_IDENTIFIER end_node /* Extension of the CT spec */ /* | T_AXIS '[' T_STRING ',' T_STRING ']'*/ towgs84: T_TOWGS84 begin_node towgs84_parameters end_node towgs84_parameters: seven_parameters /* Extension of the CT spec */ | three_parameters three_parameters: dx ',' dy ',' dz seven_parameters: dx ',' dy ',' dz ',' ex ',' ey ',' ez ',' ppm dx: T_NUMBER dy: T_NUMBER dz: T_NUMBER ex: T_NUMBER ey: T_NUMBER ez: T_NUMBER ppm: T_NUMBER fitted_cs: T_FITTED_CS begin_node_name ',' to_base ',' base_cs end_node to_base: math_transform base_cs: coordinate_system local_cs: T_LOCAL_CS begin_node_name ',' local_datum ',' unit ',' axis opt_axis_list_authority end_node /* Extension of the CT spec: accept only name */ | T_LOCAL_CS begin_node_name end_node opt_axis_list_authority: | ',' authority | ',' axis opt_axis_list_authority local_datum: T_LOCAL_DATUM begin_node_name ',' datum_type opt_authority end_node proj-6.3.1/src/iso19111/0000775000175000017500000000000013620226607011460 500000000000000proj-6.3.1/src/iso19111/static.cpp0000664000175000017500000006155713601752712013411 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/coordinatesystem_internal.hpp" #include "proj/internal/io_internal.hpp" #include #include #include #ifndef M_PI #define M_PI 3.14159265358979323846 #endif // We put all static definitions in the same compilation unit, and in // increasing order of dependency, to avoid the "static initialization fiasco" // See https://isocpp.org/wiki/faq/ctors#static-init-order using namespace NS_PROJ::crs; using namespace NS_PROJ::datum; using namespace NS_PROJ::io; using namespace NS_PROJ::metadata; using namespace NS_PROJ::operation; using namespace NS_PROJ::util; NS_PROJ_START // --------------------------------------------------------------------------- /** \brief Key to set the authority citation of a metadata::Identifier. * * The value is to be provided as a string or a metadata::Citation. */ const std::string Identifier::AUTHORITY_KEY("authority"); /** \brief Key to set the code of a metadata::Identifier. * * The value is to be provided as a integer or a string. */ const std::string Identifier::CODE_KEY("code"); /** \brief Key to set the organization responsible for definition and * maintenance of the code of a metadata::Identifier. * * The value is to be provided as a string. */ const std::string Identifier::CODESPACE_KEY("codespace"); /** \brief Key to set the version identifier for the namespace of a * metadata::Identifier. * * The value is to be provided as a string. */ const std::string Identifier::VERSION_KEY("version"); /** \brief Key to set the natural language description of the meaning of the * code value of a metadata::Identifier. * * The value is to be provided as a string. */ const std::string Identifier::DESCRIPTION_KEY("description"); /** \brief Key to set the URI of a metadata::Identifier. * * The value is to be provided as a string. */ const std::string Identifier::URI_KEY("uri"); /** \brief EPSG codespace. */ const std::string Identifier::EPSG("EPSG"); /** \brief OGC codespace. */ const std::string Identifier::OGC("OGC"); // --------------------------------------------------------------------------- /** \brief Key to set the name of a common::IdentifiedObject * * The value is to be provided as a string or metadata::IdentifierNNPtr. */ const std::string common::IdentifiedObject::NAME_KEY("name"); /** \brief Key to set the identifier(s) of a common::IdentifiedObject * * The value is to be provided as a common::IdentifierNNPtr or a * util::ArrayOfBaseObjectNNPtr * of common::IdentifierNNPtr. */ const std::string common::IdentifiedObject::IDENTIFIERS_KEY("identifiers"); /** \brief Key to set the alias(es) of a common::IdentifiedObject * * The value is to be provided as string, a util::GenericNameNNPtr or a * util::ArrayOfBaseObjectNNPtr * of util::GenericNameNNPtr. */ const std::string common::IdentifiedObject::ALIAS_KEY("alias"); /** \brief Key to set the remarks of a common::IdentifiedObject * * The value is to be provided as a string. */ const std::string common::IdentifiedObject::REMARKS_KEY("remarks"); /** \brief Key to set the deprecation flag of a common::IdentifiedObject * * The value is to be provided as a boolean. */ const std::string common::IdentifiedObject::DEPRECATED_KEY("deprecated"); // --------------------------------------------------------------------------- /** \brief Key to set the scope of a common::ObjectUsage * * The value is to be provided as a string. */ const std::string common::ObjectUsage::SCOPE_KEY("scope"); /** \brief Key to set the domain of validity of a common::ObjectUsage * * The value is to be provided as a common::ExtentNNPtr. */ const std::string common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY("domainOfValidity"); /** \brief Key to set the object domain(s) of a common::ObjectUsage * * The value is to be provided as a common::ObjectDomainNNPtr or a * util::ArrayOfBaseObjectNNPtr * of common::ObjectDomainNNPtr. */ const std::string common::ObjectUsage::OBJECT_DOMAIN_KEY("objectDomain"); // --------------------------------------------------------------------------- /** \brief World extent. */ const ExtentNNPtr Extent::WORLD(Extent::createFromBBOX(-180, -90, 180, 90, util::optional("World"))); // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::vector WKTConstants::constants_; const char *WKTConstants::createAndAddToConstantList(const char *text) { WKTConstants::constants_.push_back(text); return text; } #define DEFINE_WKT_CONSTANT(x) \ const std::string WKTConstants::x(createAndAddToConstantList(#x)) DEFINE_WKT_CONSTANT(GEOCCS); DEFINE_WKT_CONSTANT(GEOGCS); DEFINE_WKT_CONSTANT(DATUM); DEFINE_WKT_CONSTANT(UNIT); DEFINE_WKT_CONSTANT(SPHEROID); DEFINE_WKT_CONSTANT(AXIS); DEFINE_WKT_CONSTANT(PRIMEM); DEFINE_WKT_CONSTANT(AUTHORITY); DEFINE_WKT_CONSTANT(PROJCS); DEFINE_WKT_CONSTANT(PROJECTION); DEFINE_WKT_CONSTANT(PARAMETER); DEFINE_WKT_CONSTANT(VERT_CS); DEFINE_WKT_CONSTANT(VERT_DATUM); DEFINE_WKT_CONSTANT(COMPD_CS); DEFINE_WKT_CONSTANT(TOWGS84); DEFINE_WKT_CONSTANT(EXTENSION); DEFINE_WKT_CONSTANT(LOCAL_CS); DEFINE_WKT_CONSTANT(LOCAL_DATUM); DEFINE_WKT_CONSTANT(GEODCRS); DEFINE_WKT_CONSTANT(LENGTHUNIT); DEFINE_WKT_CONSTANT(ANGLEUNIT); DEFINE_WKT_CONSTANT(SCALEUNIT); DEFINE_WKT_CONSTANT(TIMEUNIT); DEFINE_WKT_CONSTANT(ELLIPSOID); const std::string WKTConstants::CS_(createAndAddToConstantList("CS")); DEFINE_WKT_CONSTANT(ID); DEFINE_WKT_CONSTANT(PROJCRS); DEFINE_WKT_CONSTANT(BASEGEODCRS); DEFINE_WKT_CONSTANT(MERIDIAN); DEFINE_WKT_CONSTANT(ORDER); DEFINE_WKT_CONSTANT(ANCHOR); DEFINE_WKT_CONSTANT(CONVERSION); DEFINE_WKT_CONSTANT(METHOD); DEFINE_WKT_CONSTANT(REMARK); DEFINE_WKT_CONSTANT(GEOGCRS); DEFINE_WKT_CONSTANT(BASEGEOGCRS); DEFINE_WKT_CONSTANT(SCOPE); DEFINE_WKT_CONSTANT(AREA); DEFINE_WKT_CONSTANT(BBOX); DEFINE_WKT_CONSTANT(CITATION); DEFINE_WKT_CONSTANT(URI); DEFINE_WKT_CONSTANT(VERTCRS); DEFINE_WKT_CONSTANT(VDATUM); DEFINE_WKT_CONSTANT(COMPOUNDCRS); DEFINE_WKT_CONSTANT(PARAMETERFILE); DEFINE_WKT_CONSTANT(COORDINATEOPERATION); DEFINE_WKT_CONSTANT(SOURCECRS); DEFINE_WKT_CONSTANT(TARGETCRS); DEFINE_WKT_CONSTANT(INTERPOLATIONCRS); DEFINE_WKT_CONSTANT(OPERATIONACCURACY); DEFINE_WKT_CONSTANT(CONCATENATEDOPERATION); DEFINE_WKT_CONSTANT(STEP); DEFINE_WKT_CONSTANT(BOUNDCRS); DEFINE_WKT_CONSTANT(ABRIDGEDTRANSFORMATION); DEFINE_WKT_CONSTANT(DERIVINGCONVERSION); DEFINE_WKT_CONSTANT(TDATUM); DEFINE_WKT_CONSTANT(CALENDAR); DEFINE_WKT_CONSTANT(TIMEORIGIN); DEFINE_WKT_CONSTANT(TIMECRS); DEFINE_WKT_CONSTANT(VERTICALEXTENT); DEFINE_WKT_CONSTANT(TIMEEXTENT); DEFINE_WKT_CONSTANT(USAGE); DEFINE_WKT_CONSTANT(DYNAMIC); DEFINE_WKT_CONSTANT(FRAMEEPOCH); DEFINE_WKT_CONSTANT(MODEL); DEFINE_WKT_CONSTANT(VELOCITYGRID); DEFINE_WKT_CONSTANT(ENSEMBLE); DEFINE_WKT_CONSTANT(MEMBER); DEFINE_WKT_CONSTANT(ENSEMBLEACCURACY); DEFINE_WKT_CONSTANT(DERIVEDPROJCRS); DEFINE_WKT_CONSTANT(BASEPROJCRS); DEFINE_WKT_CONSTANT(EDATUM); DEFINE_WKT_CONSTANT(ENGCRS); DEFINE_WKT_CONSTANT(PDATUM); DEFINE_WKT_CONSTANT(PARAMETRICCRS); DEFINE_WKT_CONSTANT(PARAMETRICUNIT); DEFINE_WKT_CONSTANT(BASEVERTCRS); DEFINE_WKT_CONSTANT(BASEENGCRS); DEFINE_WKT_CONSTANT(BASEPARAMCRS); DEFINE_WKT_CONSTANT(BASETIMECRS); DEFINE_WKT_CONSTANT(VERSION); DEFINE_WKT_CONSTANT(GEOIDMODEL); DEFINE_WKT_CONSTANT(GEODETICCRS); DEFINE_WKT_CONSTANT(GEODETICDATUM); DEFINE_WKT_CONSTANT(PROJECTEDCRS); DEFINE_WKT_CONSTANT(PRIMEMERIDIAN); DEFINE_WKT_CONSTANT(GEOGRAPHICCRS); DEFINE_WKT_CONSTANT(TRF); DEFINE_WKT_CONSTANT(VERTICALCRS); DEFINE_WKT_CONSTANT(VERTICALDATUM); DEFINE_WKT_CONSTANT(VRF); DEFINE_WKT_CONSTANT(TIMEDATUM); DEFINE_WKT_CONSTANT(TEMPORALQUANTITY); DEFINE_WKT_CONSTANT(ENGINEERINGDATUM); DEFINE_WKT_CONSTANT(ENGINEERINGCRS); DEFINE_WKT_CONSTANT(PARAMETRICDATUM); //! @endcond // --------------------------------------------------------------------------- namespace common { /** \brief "Empty"/"None", unit of measure of type NONE. */ const UnitOfMeasure UnitOfMeasure::NONE("", 1.0, UnitOfMeasure::Type::NONE); /** \brief Scale unity, unit of measure of type SCALE. */ const UnitOfMeasure UnitOfMeasure::SCALE_UNITY("unity", 1.0, UnitOfMeasure::Type::SCALE, Identifier::EPSG, "9201"); /** \brief Parts-per-million, unit of measure of type SCALE. */ const UnitOfMeasure UnitOfMeasure::PARTS_PER_MILLION("parts per million", 1e-6, UnitOfMeasure::Type::SCALE, Identifier::EPSG, "9202"); /** \brief Metre, unit of measure of type LINEAR (SI unit). */ const UnitOfMeasure UnitOfMeasure::METRE("metre", 1.0, UnitOfMeasure::Type::LINEAR, Identifier::EPSG, "9001"); /** \brief Degree, unit of measure of type ANGULAR. */ const UnitOfMeasure UnitOfMeasure::DEGREE("degree", M_PI / 180., UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "9122"); /** \brief Arc-second, unit of measure of type ANGULAR. */ const UnitOfMeasure UnitOfMeasure::ARC_SECOND("arc-second", M_PI / 180. / 3600., UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "9104"); /** \brief Grad, unit of measure of type ANGULAR. */ const UnitOfMeasure UnitOfMeasure::GRAD("grad", M_PI / 200., UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "9105"); /** \brief Radian, unit of measure of type ANGULAR (SI unit). */ const UnitOfMeasure UnitOfMeasure::RADIAN("radian", 1.0, UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "9101"); /** \brief Microradian, unit of measure of type ANGULAR. */ const UnitOfMeasure UnitOfMeasure::MICRORADIAN("microradian", 1e-6, UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "9109"); /** \brief Second, unit of measure of type TIME (SI unit). */ const UnitOfMeasure UnitOfMeasure::SECOND("second", 1.0, UnitOfMeasure::Type::TIME, Identifier::EPSG, "1040"); /** \brief Year, unit of measure of type TIME */ const UnitOfMeasure UnitOfMeasure::YEAR("year", 31556925.445, UnitOfMeasure::Type::TIME, Identifier::EPSG, "1029"); /** \brief Metre per year, unit of measure of type LINEAR. */ const UnitOfMeasure UnitOfMeasure::METRE_PER_YEAR("metres per year", 1.0 / 31556925.445, UnitOfMeasure::Type::LINEAR, Identifier::EPSG, "1042"); /** \brief Arc-second per year, unit of measure of type ANGULAR. */ const UnitOfMeasure UnitOfMeasure::ARC_SECOND_PER_YEAR( "arc-seconds per year", M_PI / 180. / 3600. / 31556925.445, UnitOfMeasure::Type::ANGULAR, Identifier::EPSG, "1043"); /** \brief Part-sper-million per year, unit of measure of type SCALE. */ const UnitOfMeasure UnitOfMeasure::PPM_PER_YEAR("parts per million per year", 1e-6 / 31556925.445, UnitOfMeasure::Type::SCALE, Identifier::EPSG, "1036"); } // namespace common // --------------------------------------------------------------------------- namespace cs { std::map AxisDirection::registry; /** Axis positive direction is north. In a geodetic or projected CRS, north is * defined through the geodetic reference frame. In an engineering CRS, north * may be defined with respect to an engineering object rather than a * geographical direction. */ const AxisDirection AxisDirection::NORTH("north"); /** Axis positive direction is approximately north-north-east. */ const AxisDirection AxisDirection::NORTH_NORTH_EAST("northNorthEast"); /** Axis positive direction is approximately north-east. */ const AxisDirection AxisDirection::NORTH_EAST("northEast"); /** Axis positive direction is approximately east-north-east. */ const AxisDirection AxisDirection::EAST_NORTH_EAST("eastNorthEast"); /** Axis positive direction is 90deg clockwise from north. */ const AxisDirection AxisDirection::EAST("east"); /** Axis positive direction is approximately east-south-east. */ const AxisDirection AxisDirection::EAST_SOUTH_EAST("eastSouthEast"); /** Axis positive direction is approximately south-east. */ const AxisDirection AxisDirection::SOUTH_EAST("southEast"); /** Axis positive direction is approximately south-south-east. */ const AxisDirection AxisDirection::SOUTH_SOUTH_EAST("southSouthEast"); /** Axis positive direction is 180deg clockwise from north. */ const AxisDirection AxisDirection::SOUTH("south"); /** Axis positive direction is approximately south-south-west. */ const AxisDirection AxisDirection::SOUTH_SOUTH_WEST("southSouthWest"); /** Axis positive direction is approximately south-west. */ const AxisDirection AxisDirection::SOUTH_WEST("southWest"); /** Axis positive direction is approximately west-south-west. */ const AxisDirection AxisDirection::WEST_SOUTH_WEST("westSouthWest"); /** Axis positive direction is 270deg clockwise from north. */ const AxisDirection AxisDirection::WEST("west"); /** Axis positive direction is approximately west-north-west. */ const AxisDirection AxisDirection::WEST_NORTH_WEST("westNorthWest"); /** Axis positive direction is approximately north-west. */ const AxisDirection AxisDirection::NORTH_WEST("northWest"); /** Axis positive direction is approximately north-north-west. */ const AxisDirection AxisDirection::NORTH_NORTH_WEST("northNorthWest"); /** Axis positive direction is up relative to gravity. */ const AxisDirection AxisDirection::UP("up"); /** Axis positive direction is down relative to gravity. */ const AxisDirection AxisDirection::DOWN("down"); /** Axis positive direction is in the equatorial plane from the centre of the * modelled Earth towards the intersection of the equator with the prime * meridian. */ const AxisDirection AxisDirection::GEOCENTRIC_X("geocentricX"); /** Axis positive direction is in the equatorial plane from the centre of the * modelled Earth towards the intersection of the equator and the meridian 90deg * eastwards from the prime meridian. */ const AxisDirection AxisDirection::GEOCENTRIC_Y("geocentricY"); /** Axis positive direction is from the centre of the modelled Earth parallel to * its rotation axis and towards its north pole. */ const AxisDirection AxisDirection::GEOCENTRIC_Z("geocentricZ"); /** Axis positive direction is towards higher pixel column. */ const AxisDirection AxisDirection::COLUMN_POSITIVE("columnPositive"); /** Axis positive direction is towards lower pixel column. */ const AxisDirection AxisDirection::COLUMN_NEGATIVE("columnNegative"); /** Axis positive direction is towards higher pixel row. */ const AxisDirection AxisDirection::ROW_POSITIVE("rowPositive"); /** Axis positive direction is towards lower pixel row. */ const AxisDirection AxisDirection::ROW_NEGATIVE("rowNegative"); /** Axis positive direction is right in display. */ const AxisDirection AxisDirection::DISPLAY_RIGHT("displayRight"); /** Axis positive direction is left in display. */ const AxisDirection AxisDirection::DISPLAY_LEFT("displayLeft"); /** Axis positive direction is towards top of approximately vertical display * surface. */ const AxisDirection AxisDirection::DISPLAY_UP("displayUp"); /** Axis positive direction is towards bottom of approximately vertical display * surface. */ const AxisDirection AxisDirection::DISPLAY_DOWN("displayDown"); /** Axis positive direction is forward; for an observer at the centre of the * object this is will be towards its front, bow or nose. */ const AxisDirection AxisDirection::FORWARD("forward"); /** Axis positive direction is aft; for an observer at the centre of the object * this will be towards its back, stern or tail. */ const AxisDirection AxisDirection::AFT("aft"); /** Axis positive direction is port; for an observer at the centre of the object * this will be towards its left. */ const AxisDirection AxisDirection::PORT("port"); /** Axis positive direction is starboard; for an observer at the centre of the * object this will be towards its right. */ const AxisDirection AxisDirection::STARBOARD("starboard"); /** Axis positive direction is clockwise from a specified direction. */ const AxisDirection AxisDirection::CLOCKWISE("clockwise"); /** Axis positive direction is counter clockwise from a specified direction. */ const AxisDirection AxisDirection::COUNTER_CLOCKWISE("counterClockwise"); /** Axis positive direction is towards the object. */ const AxisDirection AxisDirection::TOWARDS("towards"); /** Axis positive direction is away from the object. */ const AxisDirection AxisDirection::AWAY_FROM("awayFrom"); /** Temporal axis positive direction is towards the future. */ const AxisDirection AxisDirection::FUTURE("future"); /** Temporal axis positive direction is towards the past. */ const AxisDirection AxisDirection::PAST("past"); /** Axis positive direction is unspecified. */ const AxisDirection AxisDirection::UNSPECIFIED("unspecified"); // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::map AxisDirectionWKT1::registry; const AxisDirectionWKT1 AxisDirectionWKT1::NORTH("NORTH"); const AxisDirectionWKT1 AxisDirectionWKT1::EAST("EAST"); const AxisDirectionWKT1 AxisDirectionWKT1::SOUTH("SOUTH"); const AxisDirectionWKT1 AxisDirectionWKT1::WEST("WEST"); const AxisDirectionWKT1 AxisDirectionWKT1::UP("UP"); const AxisDirectionWKT1 AxisDirectionWKT1::DOWN("DOWN"); const AxisDirectionWKT1 AxisDirectionWKT1::OTHER("OTHER"); //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const std::string AxisName::Longitude("Longitude"); const std::string AxisName::Latitude("Latitude"); const std::string AxisName::Easting("Easting"); const std::string AxisName::Northing("Northing"); const std::string AxisName::Westing("Westing"); const std::string AxisName::Southing("Southing"); const std::string AxisName::Ellipsoidal_height("Ellipsoidal height"); const std::string AxisName::Geocentric_X("Geocentric X"); const std::string AxisName::Geocentric_Y("Geocentric Y"); const std::string AxisName::Geocentric_Z("Geocentric Z"); //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const std::string AxisAbbreviation::lon("lon"); const std::string AxisAbbreviation::lat("lat"); const std::string AxisAbbreviation::E("E"); const std::string AxisAbbreviation::N("N"); const std::string AxisAbbreviation::h("h"); const std::string AxisAbbreviation::X("X"); const std::string AxisAbbreviation::Y("Y"); const std::string AxisAbbreviation::Z("Z"); //! @endcond } // namespace cs // --------------------------------------------------------------------------- /** \brief The realization is by adjustment of a levelling network fixed to one * or more tide gauges. */ const RealizationMethod RealizationMethod::LEVELLING("levelling"); /** \brief The realization is through a geoid height model or a height * correction model. This is applied to a specified geodetic CRS. */ const RealizationMethod RealizationMethod::GEOID("geoid"); /** \brief The realization is through a tidal model or by tidal predictions. */ const RealizationMethod RealizationMethod::TIDAL("tidal"); // --------------------------------------------------------------------------- /** \brief The Greenwich PrimeMeridian */ const PrimeMeridianNNPtr PrimeMeridian::GREENWICH(PrimeMeridian::createGREENWICH()); /** \brief The "Reference Meridian" PrimeMeridian. * * This is a meridian of longitude 0 to be used with non-Earth bodies. */ const PrimeMeridianNNPtr PrimeMeridian::REFERENCE_MERIDIAN( PrimeMeridian::createREFERENCE_MERIDIAN()); /** \brief The Paris PrimeMeridian */ const PrimeMeridianNNPtr PrimeMeridian::PARIS(PrimeMeridian::createPARIS()); // --------------------------------------------------------------------------- /** \brief Earth celestial body */ const std::string Ellipsoid::EARTH("Earth"); /** \brief The EPSG:7008 / "Clarke 1866" Ellipsoid */ const EllipsoidNNPtr Ellipsoid::CLARKE_1866(Ellipsoid::createCLARKE_1866()); /** \brief The EPSG:7030 / "WGS 84" Ellipsoid */ const EllipsoidNNPtr Ellipsoid::WGS84(Ellipsoid::createWGS84()); /** \brief The EPSG:7019 / "GRS 1980" Ellipsoid */ const EllipsoidNNPtr Ellipsoid::GRS1980(Ellipsoid::createGRS1980()); // --------------------------------------------------------------------------- /** \brief The EPSG:6267 / "North_American_Datum_1927" GeodeticReferenceFrame */ const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::EPSG_6267( GeodeticReferenceFrame::createEPSG_6267()); /** \brief The EPSG:6269 / "North_American_Datum_1983" GeodeticReferenceFrame */ const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::EPSG_6269( GeodeticReferenceFrame::createEPSG_6269()); /** \brief The EPSG:6326 / "WGS_1984" GeodeticReferenceFrame */ const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::EPSG_6326( GeodeticReferenceFrame::createEPSG_6326()); // --------------------------------------------------------------------------- /** \brief The proleptic Gregorian calendar. */ const std::string TemporalDatum::CALENDAR_PROLEPTIC_GREGORIAN("proleptic Gregorian"); // --------------------------------------------------------------------------- /** \brief EPSG:4978 / "WGS 84" Geocentric */ const GeodeticCRSNNPtr GeodeticCRS::EPSG_4978(GeodeticCRS::createEPSG_4978()); // --------------------------------------------------------------------------- /** \brief EPSG:4267 / "NAD27" 2D GeographicCRS */ const GeographicCRSNNPtr GeographicCRS::EPSG_4267(GeographicCRS::createEPSG_4267()); /** \brief EPSG:4269 / "NAD83" 2D GeographicCRS */ const GeographicCRSNNPtr GeographicCRS::EPSG_4269(GeographicCRS::createEPSG_4269()); /** \brief EPSG:4326 / "WGS 84" 2D GeographicCRS */ const GeographicCRSNNPtr GeographicCRS::EPSG_4326(GeographicCRS::createEPSG_4326()); /** \brief OGC:CRS84 / "CRS 84" 2D GeographicCRS (long, lat)*/ const GeographicCRSNNPtr GeographicCRS::OGC_CRS84(GeographicCRS::createOGC_CRS84()); /** \brief EPSG:4807 / "NTF (Paris)" 2D GeographicCRS */ const GeographicCRSNNPtr GeographicCRS::EPSG_4807(GeographicCRS::createEPSG_4807()); /** \brief EPSG:4979 / "WGS 84" 3D GeographicCRS */ const GeographicCRSNNPtr GeographicCRS::EPSG_4979(GeographicCRS::createEPSG_4979()); // --------------------------------------------------------------------------- /** \brief Key to set the operation version of a operation::CoordinateOperation * * The value is to be provided as a string. */ const std::string operation::CoordinateOperation::OPERATION_VERSION_KEY("operationVersion"); // --------------------------------------------------------------------------- NS_PROJ_END proj-6.3.1/src/iso19111/crs.cpp0000664000175000017500000065773313617003642012716 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif //! @cond Doxygen_Suppress #define DO_NOT_DEFINE_EXTERN_DERIVED_CRS_TEMPLATE //! @endcond #include "proj/crs.hpp" #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" #include "proj/coordinatesystem.hpp" #include "proj/io.hpp" #include "proj/util.hpp" #include "proj/internal/coordinatesystem_internal.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj_constants.h" #include #include #include #include #include #include #include #include using namespace NS_PROJ::internal; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace crs { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CRS::Private { BoundCRSPtr canonicalBoundCRS_{}; std::string extensionProj4_{}; bool implicitCS_ = false; void setImplicitCS(const util::PropertyMap &properties) { const auto pVal = properties.get("IMPLICIT_CS"); if (pVal) { if (const auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == util::BoxedValue::Type::BOOLEAN && genVal->booleanValue()) { implicitCS_ = true; } } } } }; //! @endcond // --------------------------------------------------------------------------- CRS::CRS() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- CRS::CRS(const CRS &other) : ObjectUsage(other), d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRS::~CRS() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the BoundCRS potentially attached to this CRS. * * In the case this method is called on a object returned by * BoundCRS::baseCRSWithCanonicalBoundCRS(), this method will return this * BoundCRS * * @return a BoundCRSPtr, that might be null. */ const BoundCRSPtr &CRS::canonicalBoundCRS() PROJ_PURE_DEFN { return d->canonicalBoundCRS_; } // --------------------------------------------------------------------------- /** \brief Return the GeodeticCRS of the CRS. * * Returns the GeodeticCRS contained in a CRS. This works currently with * input parameters of type GeodeticCRS or derived, ProjectedCRS, * CompoundCRS or BoundCRS. * * @return a GeodeticCRSPtr, that might be null. */ GeodeticCRSPtr CRS::extractGeodeticCRS() const { auto raw = extractGeodeticCRSRaw(); if (raw) { return std::dynamic_pointer_cast( raw->shared_from_this().as_nullable()); } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const GeodeticCRS *CRS::extractGeodeticCRSRaw() const { auto geodCRS = dynamic_cast(this); if (geodCRS) { return geodCRS; } auto projCRS = dynamic_cast(this); if (projCRS) { return projCRS->baseCRS()->extractGeodeticCRSRaw(); } auto compoundCRS = dynamic_cast(this); if (compoundCRS) { for (const auto &subCrs : compoundCRS->componentReferenceSystems()) { auto retGeogCRS = subCrs->extractGeodeticCRSRaw(); if (retGeogCRS) { return retGeogCRS; } } } auto boundCRS = dynamic_cast(this); if (boundCRS) { return boundCRS->baseCRS()->extractGeodeticCRSRaw(); } return nullptr; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const std::string &CRS::getExtensionProj4() const noexcept { return d->extensionProj4_; } //! @endcond // --------------------------------------------------------------------------- /** \brief Return the GeographicCRS of the CRS. * * Returns the GeographicCRS contained in a CRS. This works currently with * input parameters of type GeographicCRS or derived, ProjectedCRS, * CompoundCRS or BoundCRS. * * @return a GeographicCRSPtr, that might be null. */ GeographicCRSPtr CRS::extractGeographicCRS() const { auto raw = extractGeodeticCRSRaw(); if (raw) { return std::dynamic_pointer_cast( raw->shared_from_this().as_nullable()); } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createPropertyMap(const common::IdentifiedObject *obj) { auto props = util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, obj->nameStr()); if (obj->isDeprecated()) { props.set(common::IdentifiedObject::DEPRECATED_KEY, true); } return props; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRSNNPtr CRS::alterGeodeticCRS(const GeodeticCRSNNPtr &newGeodCRS) const { auto geodCRS = dynamic_cast(this); if (geodCRS) { return newGeodCRS; } auto projCRS = dynamic_cast(this); if (projCRS) { return ProjectedCRS::create(createPropertyMap(this), newGeodCRS, projCRS->derivingConversion(), projCRS->coordinateSystem()); } auto compoundCRS = dynamic_cast(this); if (compoundCRS) { std::vector components; for (const auto &subCrs : compoundCRS->componentReferenceSystems()) { components.emplace_back(subCrs->alterGeodeticCRS(newGeodCRS)); } return CompoundCRS::create(createPropertyMap(this), components); } return NN_NO_CHECK( std::dynamic_pointer_cast(shared_from_this().as_nullable())); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRSNNPtr CRS::alterCSLinearUnit(const common::UnitOfMeasure &unit) const { { auto projCRS = dynamic_cast(this); if (projCRS) { return ProjectedCRS::create( createPropertyMap(this), projCRS->baseCRS(), projCRS->derivingConversion(), projCRS->coordinateSystem()->alterUnit(unit)); } } { auto geodCRS = dynamic_cast(this); if (geodCRS && geodCRS->isGeocentric()) { auto cs = dynamic_cast( geodCRS->coordinateSystem().get()); assert(cs); return GeodeticCRS::create( createPropertyMap(this), geodCRS->datum(), geodCRS->datumEnsemble(), cs->alterUnit(unit)); } } { auto geogCRS = dynamic_cast(this); if (geogCRS && geogCRS->coordinateSystem()->axisList().size() == 3) { return GeographicCRS::create( createPropertyMap(this), geogCRS->datum(), geogCRS->datumEnsemble(), geogCRS->coordinateSystem()->alterLinearUnit(unit)); } } { auto vertCRS = dynamic_cast(this); if (vertCRS) { return VerticalCRS::create( createPropertyMap(this), vertCRS->datum(), vertCRS->datumEnsemble(), vertCRS->coordinateSystem()->alterUnit(unit)); } } { auto engCRS = dynamic_cast(this); if (engCRS) { auto cartCS = util::nn_dynamic_pointer_cast( engCRS->coordinateSystem()); if (cartCS) { auto props = createPropertyMap(this); props.set("FORCE_OUTPUT_CS", true); return EngineeringCRS::create(props, engCRS->datum(), cartCS->alterUnit(unit)); } else { auto vertCS = util::nn_dynamic_pointer_cast( engCRS->coordinateSystem()); if (vertCS) { auto props = createPropertyMap(this); props.set("FORCE_OUTPUT_CS", true); return EngineeringCRS::create(props, engCRS->datum(), vertCS->alterUnit(unit)); } } } } return NN_NO_CHECK( std::dynamic_pointer_cast(shared_from_this().as_nullable())); } //! @endcond // --------------------------------------------------------------------------- /** \brief Return the VerticalCRS of the CRS. * * Returns the VerticalCRS contained in a CRS. This works currently with * input parameters of type VerticalCRS or derived, CompoundCRS or BoundCRS. * * @return a VerticalCRSPtr, that might be null. */ VerticalCRSPtr CRS::extractVerticalCRS() const { auto vertCRS = dynamic_cast(this); if (vertCRS) { return std::dynamic_pointer_cast( shared_from_this().as_nullable()); } auto compoundCRS = dynamic_cast(this); if (compoundCRS) { for (const auto &subCrs : compoundCRS->componentReferenceSystems()) { auto retVertCRS = subCrs->extractVerticalCRS(); if (retVertCRS) { return retVertCRS; } } } auto boundCRS = dynamic_cast(this); if (boundCRS) { return boundCRS->baseCRS()->extractVerticalCRS(); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Returns potentially * a BoundCRS, with a transformation to EPSG:4326, wrapping this CRS * * If no such BoundCRS is possible, the object will be returned. * * The purpose of this method is to be able to format a PROJ.4 string with * a +towgs84 parameter or a WKT1:GDAL string with a TOWGS node. * * This method will fetch the GeographicCRS of this CRS and find a * transformation to EPSG:4326 using the domain of the validity of the main CRS. * * @return a CRS. */ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( const io::DatabaseContextPtr &dbContext, operation::CoordinateOperationContext::IntermediateCRSUse allowIntermediateCRSUse) const { auto thisAsCRS = NN_NO_CHECK( std::static_pointer_cast(shared_from_this().as_nullable())); auto boundCRS = util::nn_dynamic_pointer_cast(thisAsCRS); if (!boundCRS) { boundCRS = canonicalBoundCRS(); } if (boundCRS) { if (boundCRS->hubCRS()->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { return NN_NO_CHECK(boundCRS); } } auto geodCRS = util::nn_dynamic_pointer_cast(thisAsCRS); auto geogCRS = extractGeographicCRS(); auto hubCRS = util::nn_static_pointer_cast(GeographicCRS::EPSG_4326); if (geodCRS && !geogCRS) { if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { return thisAsCRS; } hubCRS = util::nn_static_pointer_cast(GeodeticCRS::EPSG_4978); } else if (!geogCRS || geogCRS->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { return thisAsCRS; } else { geodCRS = geogCRS; } if (!dbContext) { return thisAsCRS; } const auto &l_domains = domains(); metadata::ExtentPtr extent; if (!l_domains.empty()) { extent = l_domains[0]->domainOfValidity(); } std::string crs_authority; const auto &l_identifiers = identifiers(); // If the object has an authority, restrict the transformations to // come from that codespace too. This avoids for example EPSG:4269 // (NAD83) to use a (dubious) ESRI transformation. if (!l_identifiers.empty()) { crs_authority = *(l_identifiers[0]->codeSpace()); } auto authorities = dbContext->getAllowedAuthorities(crs_authority, "EPSG"); if (authorities.empty()) { authorities.emplace_back(); } for (const auto &authority : authorities) { try { auto authFactory = io::AuthorityFactory::create( NN_NO_CHECK(dbContext), authority == "any" ? std::string() : authority); auto ctxt = operation::CoordinateOperationContext::create( authFactory, extent, 0.0); ctxt->setAllowUseIntermediateCRS(allowIntermediateCRSUse); // ctxt->setSpatialCriterion( // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); auto list = operation::CoordinateOperationFactory::create() ->createOperations(NN_NO_CHECK(geodCRS), hubCRS, ctxt); for (const auto &op : list) { auto transf = util::nn_dynamic_pointer_cast( op); if (transf && !starts_with(transf->nameStr(), "Ballpark geo")) { try { transf->getTOWGS84Parameters(); } catch (const std::exception &) { continue; } return util::nn_static_pointer_cast(BoundCRS::create( thisAsCRS, hubCRS, NN_NO_CHECK(transf))); } else { auto concatenated = dynamic_cast( op.get()); if (concatenated) { // Case for EPSG:4807 / "NTF (Paris)" that is made of a // longitude rotation followed by a Helmert // The prime meridian shift will be accounted elsewhere const auto &subops = concatenated->operations(); if (subops.size() == 2) { auto firstOpIsTransformation = dynamic_cast( subops[0].get()); auto firstOpIsConversion = dynamic_cast( subops[0].get()); if ((firstOpIsTransformation && firstOpIsTransformation ->isLongitudeRotation()) || (dynamic_cast(thisAsCRS.get()) && firstOpIsConversion)) { transf = util::nn_dynamic_pointer_cast< operation::Transformation>(subops[1]); if (transf && !starts_with(transf->nameStr(), "Ballpark geo")) { try { transf->getTOWGS84Parameters(); } catch (const std::exception &) { continue; } return util::nn_static_pointer_cast( BoundCRS::create(thisAsCRS, hubCRS, NN_NO_CHECK(transf))); } } } } } } } catch (const std::exception &) { } } return thisAsCRS; } // --------------------------------------------------------------------------- /** \brief Returns a CRS whose coordinate system does not contain a vertical * component * * @return a CRS. */ CRSNNPtr CRS::stripVerticalComponent() const { auto self = NN_NO_CHECK( std::dynamic_pointer_cast(shared_from_this().as_nullable())); auto geogCRS = dynamic_cast(this); if (geogCRS) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); if (axisList.size() == 3) { auto cs = cs::EllipsoidalCS::create(util::PropertyMap(), axisList[0], axisList[1]); return util::nn_static_pointer_cast(GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, nameStr()), geogCRS->datum(), geogCRS->datumEnsemble(), cs)); } } auto projCRS = dynamic_cast(this); if (projCRS) { const auto &axisList = projCRS->coordinateSystem()->axisList(); if (axisList.size() == 3) { auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0], axisList[1]); return util::nn_static_pointer_cast(ProjectedCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, nameStr()), projCRS->baseCRS(), projCRS->derivingConversion(), cs)); } } return self; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress /** \brief Return a shallow clone of this object. */ CRSNNPtr CRS::shallowClone() const { return _shallowClone(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRSNNPtr CRS::alterName(const std::string &newName) const { auto crs = shallowClone(); auto newNameMod(newName); auto props = util::PropertyMap(); if (ends_with(newNameMod, " (deprecated)")) { newNameMod.resize(newNameMod.size() - strlen(" (deprecated)")); props.set(common::IdentifiedObject::DEPRECATED_KEY, true); } props.set(common::IdentifiedObject::NAME_KEY, newNameMod); crs->setProperties(props); return crs; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRSNNPtr CRS::alterId(const std::string &authName, const std::string &code) const { auto crs = shallowClone(); auto props = util::PropertyMap(); props.set(metadata::Identifier::CODESPACE_KEY, authName) .set(metadata::Identifier::CODE_KEY, code); crs->setProperties(props); return crs; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool mustAxisOrderBeSwitchedForVisualizationInternal( const std::vector &axisList) { const auto &dir0 = axisList[0]->direction(); const auto &dir1 = axisList[1]->direction(); if (&dir0 == &cs::AxisDirection::NORTH && &dir1 == &cs::AxisDirection::EAST) { return true; } // Address EPSG:32661 "WGS 84 / UPS North (N,E)" if (&dir0 == &cs::AxisDirection::SOUTH && &dir1 == &cs::AxisDirection::SOUTH) { const auto &meridian0 = axisList[0]->meridian(); const auto &meridian1 = axisList[1]->meridian(); return meridian0 != nullptr && meridian1 != nullptr && std::abs(meridian0->longitude().convertToUnit( common::UnitOfMeasure::DEGREE) - 180.0) < 1e-10 && std::abs(meridian1->longitude().convertToUnit( common::UnitOfMeasure::DEGREE) - 90.0) < 1e-10; } // Address EPSG:32761 "WGS 84 / UPS South (N,E)" if (&dir0 == &cs::AxisDirection::NORTH && &dir1 == &cs::AxisDirection::NORTH) { const auto &meridian0 = axisList[0]->meridian(); const auto &meridian1 = axisList[1]->meridian(); return meridian0 != nullptr && meridian1 != nullptr && std::abs(meridian0->longitude().convertToUnit( common::UnitOfMeasure::DEGREE) - 0.0) < 1e-10 && std::abs(meridian1->longitude().convertToUnit( common::UnitOfMeasure::DEGREE) - 90.0) < 1e-10; } return false; } // --------------------------------------------------------------------------- bool CRS::mustAxisOrderBeSwitchedForVisualization() const { const CompoundCRS *compoundCRS = dynamic_cast(this); if (compoundCRS) { const auto &comps = compoundCRS->componentReferenceSystems(); if (!comps.empty()) { return comps[0]->mustAxisOrderBeSwitchedForVisualization(); } } const GeographicCRS *geogCRS = dynamic_cast(this); if (geogCRS) { return mustAxisOrderBeSwitchedForVisualizationInternal( geogCRS->coordinateSystem()->axisList()); } const ProjectedCRS *projCRS = dynamic_cast(this); if (projCRS) { return mustAxisOrderBeSwitchedForVisualizationInternal( projCRS->coordinateSystem()->axisList()); } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CRSNNPtr CRS::normalizeForVisualization() const { auto props = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, nameStr() + " (with axis order normalized for visualization)"); const CompoundCRS *compoundCRS = dynamic_cast(this); if (compoundCRS) { const auto &comps = compoundCRS->componentReferenceSystems(); if (!comps.empty()) { std::vector newComps; newComps.emplace_back(comps[0]->normalizeForVisualization()); for (size_t i = 1; i < comps.size(); i++) { newComps.emplace_back(comps[i]); } return util::nn_static_pointer_cast( CompoundCRS::create(props, newComps)); } } const GeographicCRS *geogCRS = dynamic_cast(this); if (geogCRS) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1], axisList[0]) : cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1], axisList[0], axisList[2]); return util::nn_static_pointer_cast(GeographicCRS::create( props, geogCRS->datum(), geogCRS->datumEnsemble(), cs)); } } const ProjectedCRS *projCRS = dynamic_cast(this); if (projCRS) { const auto &axisList = projCRS->coordinateSystem()->axisList(); if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) { auto cs = axisList.size() == 2 ? cs::CartesianCS::create(util::PropertyMap(), axisList[1], axisList[0]) : cs::CartesianCS::create(util::PropertyMap(), axisList[1], axisList[0], axisList[2]); return util::nn_static_pointer_cast(ProjectedCRS::create( props, projCRS->baseCRS(), projCRS->derivingConversion(), cs)); } } return NN_NO_CHECK( std::static_pointer_cast(shared_from_this().as_nullable())); } //! @endcond // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. *
    *
  • 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * Note: in the case of a GeographicCRS whose axis * order is implicit in the input definition (for example ESRI WKT), then axis * order is ignored for the purpose of identification. That is the CRS built * from * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] * will be identified to EPSG:4326, but will not pass a * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, * but rather isEquivalentTo(EPSG_4326, * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) *
  • *
  • 90% means that CRS are equivalent, but the names are not exactly the * same.
  • *
  • 70% means that CRS are equivalent), but the names do not match at * all.
  • *
  • 25% means that the CRS are not equivalent, but there is some similarity * in * the names.
  • *
* Other confidence values may be returned by some specialized implementations. * * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and * CompoundCRS. * * @param authorityFactory Authority factory (or null, but degraded * functionality) * @return a list of matching reference CRS, and the percentage (0-100) of * confidence in the match. */ std::list> CRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { return _identify(authorityFactory); } // --------------------------------------------------------------------------- /** \brief Return CRSs that are non-deprecated substitutes for the current CRS. */ std::list CRS::getNonDeprecated(const io::DatabaseContextNNPtr &dbContext) const { std::list res; const auto &l_identifiers = identifiers(); if (l_identifiers.empty()) { return res; } const char *tableName = nullptr; if (dynamic_cast(this)) { tableName = "geodetic_crs"; } else if (dynamic_cast(this)) { tableName = "projected_crs"; } else if (dynamic_cast(this)) { tableName = "vertical_crs"; } else if (dynamic_cast(this)) { tableName = "compound_crs"; } if (!tableName) { return res; } const auto &id = l_identifiers[0]; auto tmpRes = dbContext->getNonDeprecated(tableName, *(id->codeSpace()), id->code()); for (const auto &pair : tmpRes) { res.emplace_back(io::AuthorityFactory::create(dbContext, pair.first) ->createCoordinateReferenceSystem(pair.second)); } return res; } // --------------------------------------------------------------------------- /** \brief Return a variant of this CRS "promoted" to a 3D one, if not already * the case. * * The new axis will be ellipsoidal height, oriented upwards, and with metre * units. * * @param newName Name of the new CRS. If empty, nameStr() will be used. * @param dbContext Database context to look for potentially already registered * 3D CRS. May be nullptr. * @return a new CRS promoted to 3D, or the current one if already 3D or not * applicable. * @since 6.3 */ CRSNNPtr CRS::promoteTo3D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const { const auto geogCRS = dynamic_cast(this); if (geogCRS) { const auto &axisList = geogCRS->coordinateSystem()->axisList(); if (axisList.size() == 2) { const auto &l_identifiers = identifiers(); // First check if there is a Geographic 3D CRS in the database // of the same name. // This is the common practice in the EPSG dataset. if (dbContext && l_identifiers.size() == 1) { auto authFactory = io::AuthorityFactory::create( NN_NO_CHECK(dbContext), *(l_identifiers[0]->codeSpace())); auto res = authFactory->createObjectsFromName( nameStr(), {io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS}, false); if (!res.empty()) { const auto &firstRes = res.front(); if (geogCRS->is2DPartOf3D(NN_NO_CHECK( dynamic_cast(firstRes.get())))) { return NN_NO_CHECK( util::nn_dynamic_pointer_cast(firstRes)); } } } auto upAxis = cs::CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, cs::AxisName::Ellipsoidal_height), cs::AxisAbbreviation::h, cs::AxisDirection::UP, common::UnitOfMeasure::METRE); auto cs = cs::EllipsoidalCS::create( util::PropertyMap(), axisList[0], axisList[1], upAxis); return util::nn_static_pointer_cast(GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, !newName.empty() ? newName : nameStr()), geogCRS->datum(), geogCRS->datumEnsemble(), cs)); } } const auto projCRS = dynamic_cast(this); if (projCRS) { const auto &axisList = projCRS->coordinateSystem()->axisList(); if (axisList.size() == 2) { auto base3DCRS = projCRS->baseCRS()->promoteTo3D(std::string(), dbContext); auto upAxis = cs::CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, cs::AxisName::Ellipsoidal_height), cs::AxisAbbreviation::h, cs::AxisDirection::UP, common::UnitOfMeasure::METRE); auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0], axisList[1], upAxis); return util::nn_static_pointer_cast(ProjectedCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, !newName.empty() ? newName : nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast(base3DCRS)), projCRS->derivingConversion(), cs)); } } const auto boundCRS = dynamic_cast(this); if (boundCRS) { return BoundCRS::create( boundCRS->baseCRS()->promoteTo3D(newName, dbContext), boundCRS->hubCRS(), boundCRS->transformation()); } return NN_NO_CHECK( std::static_pointer_cast(shared_from_this().as_nullable())); } // --------------------------------------------------------------------------- /** \brief Return a variant of this CRS "demoted" to a 2D one, if not already * the case. * * * @param newName Name of the new CRS. If empty, nameStr() will be used. * @param dbContext Database context to look for potentially already registered * 2D CRS. May be nullptr. * @return a new CRS demoted to 2D, or the current one if already 2D or not * applicable. * @since 6.3 */ CRSNNPtr CRS::demoteTo2D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const { const auto geogCRS = dynamic_cast(this); if (geogCRS) { return geogCRS->demoteTo2D(newName, dbContext); } const auto projCRS = dynamic_cast(this); if (projCRS) { return projCRS->demoteTo2D(newName, dbContext); } const auto boundCRS = dynamic_cast(this); if (boundCRS) { return BoundCRS::create( boundCRS->baseCRS()->demoteTo2D(newName, dbContext), boundCRS->hubCRS(), boundCRS->transformation()); } const auto compoundCRS = dynamic_cast(this); if (compoundCRS) { const auto &components = compoundCRS->componentReferenceSystems(); if (components.size() >= 2) { return components[0]; } } return NN_NO_CHECK( std::static_pointer_cast(shared_from_this().as_nullable())); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> CRS::_identify(const io::AuthorityFactoryPtr &) const { return {}; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct SingleCRS::Private { datum::DatumPtr datum{}; datum::DatumEnsemblePtr datumEnsemble{}; cs::CoordinateSystemNNPtr coordinateSystem; Private(const datum::DatumPtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::CoordinateSystemNNPtr &csIn) : datum(datumIn), datumEnsemble(datumEnsembleIn), coordinateSystem(csIn) { if ((datum ? 1 : 0) + (datumEnsemble ? 1 : 0) != 1) { throw util::Exception("datum or datumEnsemble should be set"); } } }; //! @endcond // --------------------------------------------------------------------------- SingleCRS::SingleCRS(const datum::DatumPtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::CoordinateSystemNNPtr &csIn) : d(internal::make_unique(datumIn, datumEnsembleIn, csIn)) {} // --------------------------------------------------------------------------- SingleCRS::SingleCRS(const SingleCRS &other) : CRS(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress SingleCRS::~SingleCRS() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the datum::Datum associated with the CRS. * * This might be null, in which case datumEnsemble() return will not be null. * * @return a Datum that might be null. */ const datum::DatumPtr &SingleCRS::datum() PROJ_PURE_DEFN { return d->datum; } // --------------------------------------------------------------------------- /** \brief Return the datum::DatumEnsemble associated with the CRS. * * This might be null, in which case datum() return will not be null. * * @return a DatumEnsemble that might be null. */ const datum::DatumEnsemblePtr &SingleCRS::datumEnsemble() PROJ_PURE_DEFN { return d->datumEnsemble; } // --------------------------------------------------------------------------- /** \brief Return the cs::CoordinateSystem associated with the CRS. * * @return a CoordinateSystem. */ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN { return d->coordinateSystem; } // --------------------------------------------------------------------------- bool SingleCRS::baseIsEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherSingleCRS = dynamic_cast(other); if (otherSingleCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &thisDatum = d->datum; const auto &otherDatum = otherSingleCRS->d->datum; if (thisDatum) { if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion, dbContext)) { return false; } } else { if (otherDatum) { return false; } } // TODO test DatumEnsemble return d->coordinateSystem->_isEquivalentTo( otherSingleCRS->d->coordinateSystem.get(), criterion, dbContext) && getExtensionProj4() == otherSingleCRS->getExtensionProj4(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void SingleCRS::exportDatumOrDatumEnsembleToWkt( io::WKTFormatter *formatter) const // throw(io::FormattingException) { const auto &l_datum = d->datum; if (l_datum) { l_datum->_exportToWKT(formatter); } else { const auto &l_datumEnsemble = d->datumEnsemble; assert(l_datumEnsemble); l_datumEnsemble->_exportToWKT(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeodeticCRS::Private { std::vector velocityModel{}; datum::GeodeticReferenceFramePtr datum_; explicit Private(const datum::GeodeticReferenceFramePtr &datumIn) : datum_(datumIn) {} }; // --------------------------------------------------------------------------- static const datum::DatumEnsemblePtr & checkEnsembleForGeodeticCRS(const datum::GeodeticReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &ensemble) { const char *msg = "One of Datum or DatumEnsemble should be defined"; if (datumIn) { if (!ensemble) { return ensemble; } msg = "Datum and DatumEnsemble should not be defined"; } else if (ensemble) { const auto &datums = ensemble->datums(); assert(!datums.empty()); auto grfFirst = dynamic_cast(datums[0].get()); if (grfFirst) { return ensemble; } msg = "Ensemble should contain GeodeticReferenceFrame"; } throw util::Exception(msg); } //! @endcond // --------------------------------------------------------------------------- GeodeticCRS::GeodeticCRS(const datum::GeodeticReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::EllipsoidalCSNNPtr &csIn) : SingleCRS(datumIn, checkEnsembleForGeodeticCRS(datumIn, datumEnsembleIn), csIn), d(internal::make_unique(datumIn)) {} // --------------------------------------------------------------------------- GeodeticCRS::GeodeticCRS(const datum::GeodeticReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::SphericalCSNNPtr &csIn) : SingleCRS(datumIn, checkEnsembleForGeodeticCRS(datumIn, datumEnsembleIn), csIn), d(internal::make_unique(datumIn)) {} // --------------------------------------------------------------------------- GeodeticCRS::GeodeticCRS(const datum::GeodeticReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::CartesianCSNNPtr &csIn) : SingleCRS(datumIn, checkEnsembleForGeodeticCRS(datumIn, datumEnsembleIn), csIn), d(internal::make_unique(datumIn)) {} // --------------------------------------------------------------------------- GeodeticCRS::GeodeticCRS(const GeodeticCRS &other) : SingleCRS(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeodeticCRS::~GeodeticCRS() = default; //! @endcond // --------------------------------------------------------------------------- CRSNNPtr GeodeticCRS::_shallowClone() const { auto crs(GeodeticCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the datum::GeodeticReferenceFrame associated with the CRS. * * @return a GeodeticReferenceFrame or null (in which case datumEnsemble() * should return a non-null pointer.) */ const datum::GeodeticReferenceFramePtr &GeodeticCRS::datum() PROJ_PURE_DEFN { return d->datum_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static datum::GeodeticReferenceFrame *oneDatum(const GeodeticCRS *crs) { const auto &l_datumEnsemble = crs->datumEnsemble(); assert(l_datumEnsemble); const auto &l_datums = l_datumEnsemble->datums(); return static_cast(l_datums[0].get()); } //! @endcond // --------------------------------------------------------------------------- /** \brief Return the PrimeMeridian associated with the GeodeticReferenceFrame * or with one of the GeodeticReferenceFrame of the datumEnsemble(). * * @return the PrimeMeridian. */ const datum::PrimeMeridianNNPtr &GeodeticCRS::primeMeridian() PROJ_PURE_DEFN { if (d->datum_) { return d->datum_->primeMeridian(); } return oneDatum(this)->primeMeridian(); } // --------------------------------------------------------------------------- /** \brief Return the ellipsoid associated with the GeodeticReferenceFrame * or with one of the GeodeticReferenceFrame of the datumEnsemble(). * * @return the PrimeMeridian. */ const datum::EllipsoidNNPtr &GeodeticCRS::ellipsoid() PROJ_PURE_DEFN { if (d->datum_) { return d->datum_->ellipsoid(); } return oneDatum(this)->ellipsoid(); } // --------------------------------------------------------------------------- /** \brief Return the velocity model associated with the CRS. * * @return a velocity model. might be null. */ const std::vector & GeodeticCRS::velocityModel() PROJ_PURE_DEFN { return d->velocityModel; } // --------------------------------------------------------------------------- /** \brief Return whether the CRS is a geocentric one. * * A geocentric CRS is a geodetic CRS that has a Cartesian coordinate system * with three axis, whose direction is respectively * cs::AxisDirection::GEOCENTRIC_X, * cs::AxisDirection::GEOCENTRIC_Y and cs::AxisDirection::GEOCENTRIC_Z. * * @return true if the CRS is a geocentric CRS. */ bool GeodeticCRS::isGeocentric() PROJ_PURE_DEFN { const auto &cs = coordinateSystem(); const auto &axisList = cs->axisList(); return axisList.size() == 3 && dynamic_cast(cs.get()) != nullptr && &axisList[0]->direction() == &cs::AxisDirection::GEOCENTRIC_X && &axisList[1]->direction() == &cs::AxisDirection::GEOCENTRIC_Y && &axisList[2]->direction() == &cs::AxisDirection::GEOCENTRIC_Z; } // --------------------------------------------------------------------------- /** \brief Instantiate a GeodeticCRS from a datum::GeodeticReferenceFrame and a * cs::SphericalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS. * @param cs a SphericalCS. * @return new GeodeticCRS. */ GeodeticCRSNNPtr GeodeticCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFrameNNPtr &datum, const cs::SphericalCSNNPtr &cs) { return create(properties, datum.as_nullable(), nullptr, cs); } // --------------------------------------------------------------------------- /** \brief Instantiate a GeodeticCRS from a datum::GeodeticReferenceFrame or * datum::DatumEnsemble and a cs::SphericalCS. * * One and only one of datum or datumEnsemble should be set to a non-null value. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS, or nullptr * @param datumEnsemble The datum ensemble of the CRS, or nullptr. * @param cs a SphericalCS. * @return new GeodeticCRS. */ GeodeticCRSNNPtr GeodeticCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFramePtr &datum, const datum::DatumEnsemblePtr &datumEnsemble, const cs::SphericalCSNNPtr &cs) { auto crs( GeodeticCRS::nn_make_shared(datum, datumEnsemble, cs)); crs->assignSelf(crs); crs->setProperties(properties); properties.getStringValue("EXTENSION_PROJ4", crs->CRS::getPrivate()->extensionProj4_); return crs; } // --------------------------------------------------------------------------- /** \brief Instantiate a GeodeticCRS from a datum::GeodeticReferenceFrame and a * cs::CartesianCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS. * @param cs a CartesianCS. * @return new GeodeticCRS. */ GeodeticCRSNNPtr GeodeticCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFrameNNPtr &datum, const cs::CartesianCSNNPtr &cs) { return create(properties, datum.as_nullable(), nullptr, cs); } // --------------------------------------------------------------------------- /** \brief Instantiate a GeodeticCRS from a datum::GeodeticReferenceFrame or * datum::DatumEnsemble and a cs::CartesianCS. * * One and only one of datum or datumEnsemble should be set to a non-null value. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS, or nullptr * @param datumEnsemble The datum ensemble of the CRS, or nullptr. * @param cs a CartesianCS * @return new GeodeticCRS. */ GeodeticCRSNNPtr GeodeticCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFramePtr &datum, const datum::DatumEnsemblePtr &datumEnsemble, const cs::CartesianCSNNPtr &cs) { auto crs( GeodeticCRS::nn_make_shared(datum, datumEnsemble, cs)); crs->assignSelf(crs); crs->setProperties(properties); properties.getStringValue("EXTENSION_PROJ4", crs->CRS::getPrivate()->extensionProj4_); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const bool isGeographic = dynamic_cast(this) != nullptr; formatter->startNode(isWKT2 ? ((formatter->use2019Keywords() && isGeographic) ? io::WKTConstants::GEOGCRS : io::WKTConstants::GEODCRS) : isGeocentric() ? io::WKTConstants::GEOCCS : io::WKTConstants::GEOGCS, !identifiers().empty()); auto l_name = nameStr(); const auto &cs = coordinateSystem(); const auto &axisList = cs->axisList(); const auto oldAxisOutputRule = formatter->outputAxis(); if (formatter->useESRIDialect()) { if (axisList.size() != 2) { io::FormattingException::Throw( "Only export of Geographic 2D CRS is supported in WKT1_ESRI"); } if (l_name == "WGS 84") { l_name = "GCS_WGS_1984"; } else { bool aliasFound = false; const auto &dbContext = formatter->databaseContext(); if (dbContext) { auto l_alias = dbContext->getAliasFromOfficialName( l_name, "geodetic_crs", "ESRI"); if (!l_alias.empty()) { l_name = l_alias; aliasFound = true; } } if (!aliasFound) { l_name = io::WKTFormatter::morphNameToESRI(l_name); if (!starts_with(l_name, "GCS_")) { l_name = "GCS_" + l_name; } } } } else if (!isWKT2 && formatter->isStrict() && isGeographic && axisList.size() != 2 && oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) { io::FormattingException::Throw( "WKT1 does not support Geographic 3D CRS."); } if (!isWKT2 && !formatter->useESRIDialect() && isDeprecated()) { l_name += " (deprecated)"; } formatter->addQuotedString(l_name); const auto &unit = axisList[0]->unit(); formatter->pushAxisAngularUnit(common::UnitOfMeasure::create(unit)); exportDatumOrDatumEnsembleToWkt(formatter); primeMeridian()->_exportToWKT(formatter); formatter->popAxisAngularUnit(); if (!isWKT2) { unit._exportToWKT(formatter); } if (oldAxisOutputRule == io::WKTFormatter::OutputAxisRule::WKT1_GDAL_EPSG_STYLE && isGeocentric()) { formatter->setOutputAxis(io::WKTFormatter::OutputAxisRule::YES); } cs->_exportToWKT(formatter); formatter->setOutputAxis(oldAxisOutputRule); ObjectUsage::baseExportToWKT(formatter); if (!isWKT2 && !formatter->useESRIDialect()) { const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_; if (!extensionProj4.empty()) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); formatter->addQuotedString(extensionProj4); formatter->endNode(); } } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticCRS::addGeocentricUnitConversionIntoPROJString( io::PROJStringFormatter *formatter) const { const auto &axisList = coordinateSystem()->axisList(); const auto &unit = axisList[0]->unit(); if (!unit._isEquivalentTo(common::UnitOfMeasure::METRE, util::IComparable::Criterion::EQUIVALENT)) { if (formatter->getCRSExport()) { io::FormattingException::Throw( "GeodeticCRS::exportToPROJString() only " "supports metre unit"); } formatter->addStep("unitconvert"); formatter->addParam("xy_in", "m"); formatter->addParam("z_in", "m"); { auto projUnit = unit.exportToPROJString(); if (!projUnit.empty()) { formatter->addParam("xy_out", projUnit); formatter->addParam("z_out", projUnit); return; } } const auto &toSI = unit.conversionToSI(); formatter->addParam("xy_out", toSI); formatter->addParam("z_out", toSI); } else if (formatter->getCRSExport()) { formatter->addParam("units", "m"); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_; if (!extensionProj4.empty()) { formatter->ingestPROJString( replaceAll(extensionProj4, " +type=crs", "")); formatter->addNoDefs(false); return; } if (!isGeocentric()) { io::FormattingException::Throw( "GeodeticCRS::exportToPROJString() only " "supports geocentric coordinate systems"); } if (!formatter->getCRSExport()) { formatter->addStep("cart"); } else { formatter->addStep("geocent"); } addDatumInfoToPROJString(formatter); addGeocentricUnitConversionIntoPROJString(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticCRS::addDatumInfoToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { const auto &TOWGS84Params = formatter->getTOWGS84Parameters(); bool datumWritten = false; const auto &nadgrids = formatter->getHDatumExtension(); const auto &l_datum = datum(); if (formatter->getCRSExport() && l_datum && TOWGS84Params.empty() && nadgrids.empty()) { if (l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6326.get(), util::IComparable::Criterion::EQUIVALENT)) { datumWritten = true; formatter->addParam("datum", "WGS84"); } else if (l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6267.get(), util::IComparable::Criterion::EQUIVALENT)) { datumWritten = true; formatter->addParam("datum", "NAD27"); } else if (l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6269.get(), util::IComparable::Criterion::EQUIVALENT)) { datumWritten = true; if (formatter->getLegacyCRSToCRSContext()) { // We do not want datum=NAD83 to cause a useless towgs84=0,0,0 formatter->addParam("ellps", "GRS80"); } else { formatter->addParam("datum", "NAD83"); } } } if (!datumWritten) { ellipsoid()->_exportToPROJString(formatter); primeMeridian()->_exportToPROJString(formatter); } if (TOWGS84Params.size() == 7) { formatter->addParam("towgs84", TOWGS84Params); } if (!nadgrids.empty()) { formatter->addParam("nadgrids", nadgrids); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("GeodeticCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } const auto &l_datum(datum()); if (l_datum) { writer.AddObjKey("datum"); l_datum->_exportToJSON(formatter); } else { writer.AddObjKey("datum_ensemble"); formatter->setOmitTypeInImmediateChild(); datumEnsemble()->_exportToJSON(formatter); } writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::IComparable::Criterion getStandardCriterion(util::IComparable::Criterion criterion) { return criterion == util::IComparable::Criterion:: EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS ? util::IComparable::Criterion::EQUIVALENT : criterion; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool GeodeticCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { const auto standardCriterion = getStandardCriterion(criterion); auto otherGeodCRS = dynamic_cast(other); // TODO test velocityModel return otherGeodCRS != nullptr && SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createMapNameEPSGCode(const char *name, int code) { return util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, code); } //! @endcond // --------------------------------------------------------------------------- GeodeticCRSNNPtr GeodeticCRS::createEPSG_4978() { return create( createMapNameEPSGCode("WGS 84", 4978), datum::GeodeticReferenceFrame::EPSG_6326, cs::CartesianCS::createGeocentric(common::UnitOfMeasure::METRE)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool hasCodeCompatibleOfAuthorityFactory( const common::IdentifiedObject *obj, const io::AuthorityFactoryPtr &authorityFactory) { const auto &ids = obj->identifiers(); if (!ids.empty() && authorityFactory->getAuthority().empty()) { return true; } for (const auto &id : ids) { if (*(id->codeSpace()) == authorityFactory->getAuthority()) { return true; } } return false; } static bool hasCodeCompatibleOfAuthorityFactory( const metadata::IdentifierNNPtr &id, const io::AuthorityFactoryPtr &authorityFactory) { if (authorityFactory->getAuthority().empty()) { return true; } return *(id->codeSpace()) == authorityFactory->getAuthority(); } //! @endcond // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match: *
    *
  • 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * Note: in the case of a GeographicCRS whose axis * order is implicit in the input definition (for example ESRI WKT), then axis * order is ignored for the purpose of identification. That is the CRS built * from * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] * will be identified to EPSG:4326, but will not pass a * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, * but rather isEquivalentTo(EPSG_4326, * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) *
  • *
  • 90% means that CRS are equivalent, but the names are not exactly the * same. *
  • 70% means that CRS are equivalent (equivalent datum and coordinate * system), * but the names do not match at all.
  • *
  • 60% means that ellipsoid, prime meridian and coordinate systems are * equivalent, but the CRS and datum names do not match.
  • *
  • 25% means that the CRS are not equivalent, but there is some similarity * in * the names.
  • *
* * @param authorityFactory Authority factory (or null, but degraded * functionality) * @return a list of matching reference CRS, and the percentage (0-100) of * confidence in the match. */ std::list> GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; const auto &thisName(nameStr()); io::DatabaseContextPtr dbContext = authorityFactory ? authorityFactory->databaseContext().as_nullable() : nullptr; const bool l_implicitCS = CRS::getPrivate()->implicitCS_; const auto crsCriterion = l_implicitCS ? util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS : util::IComparable::Criterion::EQUIVALENT; const GeographicCRSNNPtr candidatesCRS[] = {GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4267, GeographicCRS::EPSG_4269}; for (const auto &crs : candidatesCRS) { const bool nameEquivalent = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); const bool nameEqual = thisName == crs->nameStr(); const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext); if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) { res.emplace_back(util::nn_static_pointer_cast(crs), nameEqual ? 100 : 90); return res; } else if (nameEqual && !isEq && !authorityFactory) { res.emplace_back(util::nn_static_pointer_cast(crs), 25); return res; } else if (isEq && !authorityFactory) { res.emplace_back(util::nn_static_pointer_cast(crs), 70); return res; } } std::string geodetic_crs_type; if (isGeocentric()) { geodetic_crs_type = "geocentric"; } else { auto geogCRS = dynamic_cast(this); if (geogCRS) { if (coordinateSystem()->axisList().size() == 2) { geodetic_crs_type = "geographic 2D"; } else { geodetic_crs_type = "geographic 3D"; } } } if (authorityFactory) { const auto &thisDatum(datum()); auto searchByDatum = [this, &authorityFactory, &res, &thisDatum, &geodetic_crs_type, crsCriterion, &dbContext]() { for (const auto &id : thisDatum->identifiers()) { try { auto tempRes = authorityFactory->createGeodeticCRSFromDatum( *id->codeSpace(), id->code(), geodetic_crs_type); for (const auto &crs : tempRes) { if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) { res.emplace_back(crs, 70); } } } catch (const std::exception &) { } } }; const auto &thisEllipsoid(ellipsoid()); auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum, &thisEllipsoid, &geodetic_crs_type, l_implicitCS, &dbContext]() { const auto ellipsoids = thisEllipsoid->identifiers().empty() ? authorityFactory->createEllipsoidFromExisting( thisEllipsoid) : std::list{thisEllipsoid}; for (const auto &ellps : ellipsoids) { for (const auto &id : ellps->identifiers()) { try { auto tempRes = authorityFactory->createGeodeticCRSFromEllipsoid( *id->codeSpace(), id->code(), geodetic_crs_type); for (const auto &crs : tempRes) { const auto &crsDatum(crs->datum()); if (crsDatum && crsDatum->ellipsoid()->_isEquivalentTo( ellps.get(), util::IComparable::Criterion::EQUIVALENT, dbContext) && crsDatum->primeMeridian()->_isEquivalentTo( thisDatum->primeMeridian().get(), util::IComparable::Criterion::EQUIVALENT, dbContext) && (!l_implicitCS || coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), util::IComparable::Criterion::EQUIVALENT, dbContext))) { res.emplace_back(crs, 60); } } } catch (const std::exception &) { } } } }; const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || ci_equal(thisName, "unnamed"); if (unsignificantName) { if (thisDatum) { if (!thisDatum->identifiers().empty()) { searchByDatum(); } else { searchByEllipsoid(); } } } else if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) { // If the CRS has already an id, check in the database for the // official object, and verify that they are equivalent. for (const auto &id : identifiers()) { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( authorityFactory->databaseContext(), *id->codeSpace()) ->createGeodeticCRS(id->code()); bool match = _isEquivalentTo(crs.get(), crsCriterion, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { } } } } else { bool gotAbove25Pct = false; for (int ipass = 0; ipass < 2; ipass++) { const bool approximateMatch = ipass == 1; auto objects = authorityFactory->createObjectsFromName( thisName, {io::AuthorityFactory::ObjectType::GEODETIC_CRS}, approximateMatch); for (const auto &obj : objects) { auto crs = util::nn_dynamic_pointer_cast(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); return res; } const bool eqName = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); res.emplace_back(crsNN, eqName ? 90 : 70); gotAbove25Pct = true; } else { res.emplace_back(crsNN, 25); } } if (!res.empty()) { break; } } if (!gotAbove25Pct && thisDatum) { if (!thisDatum->identifiers().empty()) { searchByDatum(); } else { searchByEllipsoid(); } } } const auto &thisCS(coordinateSystem()); // Sort results res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a, const Pair &b) { // First consider confidence if (a.second > b.second) { return true; } if (a.second < b.second) { return false; } // Then consider exact name matching const auto &aName(a.first->nameStr()); const auto &bName(b.first->nameStr()); if (aName == thisName && bName != thisName) { return true; } if (bName == thisName && aName != thisName) { return false; } // Then datum matching const auto &aDatum(a.first->datum()); const auto &bDatum(b.first->datum()); if (thisDatum && aDatum && bDatum) { const auto thisEquivADatum(thisDatum->_isEquivalentTo( aDatum.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)); const auto thisEquivBDatum(thisDatum->_isEquivalentTo( bDatum.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)); if (thisEquivADatum && !thisEquivBDatum) { return true; } if (!thisEquivADatum && thisEquivBDatum) { return false; } } // Then coordinate system matching const auto &aCS(a.first->coordinateSystem()); const auto &bCS(b.first->coordinateSystem()); const auto thisEquivACs(thisCS->_isEquivalentTo( aCS.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)); const auto thisEquivBCs(thisCS->_isEquivalentTo( bCS.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)); if (thisEquivACs && !thisEquivBCs) { return true; } if (!thisEquivACs && thisEquivBCs) { return false; } // Then dimension of the coordinate system matching const auto thisCSAxisListSize = thisCS->axisList().size(); const auto aCSAxistListSize = aCS->axisList().size(); const auto bCSAxistListSize = bCS->axisList().size(); if (thisCSAxisListSize == aCSAxistListSize && thisCSAxisListSize != bCSAxistListSize) { return true; } if (thisCSAxisListSize != aCSAxistListSize && thisCSAxisListSize == bCSAxistListSize) { return false; } if (aDatum && bDatum) { // Favor the CRS whole ellipsoid names matches the ellipsoid // name (WGS84...) const bool aEllpsNameEqCRSName = metadata::Identifier::isEquivalentName( aDatum->ellipsoid()->nameStr().c_str(), a.first->nameStr().c_str()); const bool bEllpsNameEqCRSName = metadata::Identifier::isEquivalentName( bDatum->ellipsoid()->nameStr().c_str(), b.first->nameStr().c_str()); if (aEllpsNameEqCRSName && !bEllpsNameEqCRSName) { return true; } if (bEllpsNameEqCRSName && !aEllpsNameEqCRSName) { return false; } } // Arbitrary final sorting criterion return aName < bName; }); // If there are results with 90% confidence, only keep those if (res.size() >= 2 && res.front().second == 90) { std::list newRes; for (const auto &pair : res) { if (pair.second == 90) { newRes.push_back(pair); } else { break; } } return newRes; } } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> GeodeticCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; auto resTemp = identify(authorityFactory); for (const auto &pair : resTemp) { res.emplace_back(pair.first, pair.second); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeographicCRS::Private { cs::EllipsoidalCSNNPtr coordinateSystem_; explicit Private(const cs::EllipsoidalCSNNPtr &csIn) : coordinateSystem_(csIn) {} }; //! @endcond // --------------------------------------------------------------------------- GeographicCRS::GeographicCRS(const datum::GeodeticReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::EllipsoidalCSNNPtr &csIn) : SingleCRS(datumIn, datumEnsembleIn, csIn), GeodeticCRS(datumIn, checkEnsembleForGeodeticCRS(datumIn, datumEnsembleIn), csIn), d(internal::make_unique(csIn)) {} // --------------------------------------------------------------------------- GeographicCRS::GeographicCRS(const GeographicCRS &other) : SingleCRS(other), GeodeticCRS(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeographicCRS::~GeographicCRS() = default; //! @endcond // --------------------------------------------------------------------------- CRSNNPtr GeographicCRS::_shallowClone() const { auto crs(GeographicCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the cs::EllipsoidalCS associated with the CRS. * * @return a EllipsoidalCS. */ const cs::EllipsoidalCSNNPtr &GeographicCRS::coordinateSystem() PROJ_PURE_DEFN { return d->coordinateSystem_; } // --------------------------------------------------------------------------- /** \brief Instantiate a GeographicCRS from a datum::GeodeticReferenceFrameNNPtr * and a * cs::EllipsoidalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS. * @param cs a EllipsoidalCS. * @return new GeographicCRS. */ GeographicCRSNNPtr GeographicCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFrameNNPtr &datum, const cs::EllipsoidalCSNNPtr &cs) { return create(properties, datum.as_nullable(), nullptr, cs); } // --------------------------------------------------------------------------- /** \brief Instantiate a GeographicCRS from a datum::GeodeticReferenceFramePtr * or * datum::DatumEnsemble and a * cs::EllipsoidalCS. * * One and only one of datum or datumEnsemble should be set to a non-null value. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datum The datum of the CRS, or nullptr * @param datumEnsemble The datum ensemble of the CRS, or nullptr. * @param cs a EllipsoidalCS. * @return new GeographicCRS. */ GeographicCRSNNPtr GeographicCRS::create(const util::PropertyMap &properties, const datum::GeodeticReferenceFramePtr &datum, const datum::DatumEnsemblePtr &datumEnsemble, const cs::EllipsoidalCSNNPtr &cs) { GeographicCRSNNPtr crs( GeographicCRS::nn_make_shared(datum, datumEnsemble, cs)); crs->assignSelf(crs); crs->setProperties(properties); properties.getStringValue("EXTENSION_PROJ4", crs->CRS::getPrivate()->extensionProj4_); crs->CRS::getPrivate()->setImplicitCS(properties); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress /** \brief Return whether the current GeographicCRS is the 2D part of the * other 3D GeographicCRS. */ bool GeographicCRS::is2DPartOf3D(util::nn other) PROJ_PURE_DEFN { const auto &axis = d->coordinateSystem_->axisList(); const auto &otherAxis = other->d->coordinateSystem_->axisList(); if (!(axis.size() == 2 && otherAxis.size() == 3)) { return false; } const auto &firstAxis = axis[0]; const auto &secondAxis = axis[1]; const auto &otherFirstAxis = otherAxis[0]; const auto &otherSecondAxis = otherAxis[1]; if (!(firstAxis->_isEquivalentTo( otherFirstAxis.get(), util::IComparable::Criterion::EQUIVALENT) && secondAxis->_isEquivalentTo( otherSecondAxis.get(), util::IComparable::Criterion::EQUIVALENT))) { return false; } const auto &thisDatum = GeodeticCRS::getPrivate()->datum_; const auto &otherDatum = other->GeodeticCRS::getPrivate()->datum_; if (thisDatum && otherDatum) { return thisDatum->_isEquivalentTo( otherDatum.get(), util::IComparable::Criterion::EQUIVALENT); } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool GeographicCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherGeogCRS = dynamic_cast(other); if (otherGeogCRS == nullptr) { return false; } const auto standardCriterion = getStandardCriterion(criterion); if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) { return true; } if (criterion != util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) { return false; } const auto axisOrder = coordinateSystem()->axisOrder(); if (axisOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH || axisOrder == cs::EllipsoidalCS::AxisOrder::LAT_NORTH_LONG_EAST) { const auto &unit = coordinateSystem()->axisList()[0]->unit(); return GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, nameStr()), datum(), datumEnsemble(), axisOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH ? cs::EllipsoidalCS::createLatitudeLongitude(unit) : cs::EllipsoidalCS::createLongitudeLatitude(unit)) ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext); } return false; } //! @endcond // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createEPSG_4267() { return create(createMapNameEPSGCode("NAD27", 4267), datum::GeodeticReferenceFrame::EPSG_6267, cs::EllipsoidalCS::createLatitudeLongitude( common::UnitOfMeasure::DEGREE)); } // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createEPSG_4269() { return create(createMapNameEPSGCode("NAD83", 4269), datum::GeodeticReferenceFrame::EPSG_6269, cs::EllipsoidalCS::createLatitudeLongitude( common::UnitOfMeasure::DEGREE)); } // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createEPSG_4326() { return create(createMapNameEPSGCode("WGS 84", 4326), datum::GeodeticReferenceFrame::EPSG_6326, cs::EllipsoidalCS::createLatitudeLongitude( common::UnitOfMeasure::DEGREE)); } // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createOGC_CRS84() { util::PropertyMap propertiesCRS; propertiesCRS .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::OGC) .set(metadata::Identifier::CODE_KEY, "CRS84") .set(common::IdentifiedObject::NAME_KEY, "WGS 84 (CRS84)"); return create(propertiesCRS, datum::GeodeticReferenceFrame::EPSG_6326, cs::EllipsoidalCS::createLongitudeLatitude( // Long Lat ! common::UnitOfMeasure::DEGREE)); } // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createEPSG_4979() { return create( createMapNameEPSGCode("WGS 84", 4979), datum::GeodeticReferenceFrame::EPSG_6326, cs::EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight( common::UnitOfMeasure::DEGREE, common::UnitOfMeasure::METRE)); } // --------------------------------------------------------------------------- GeographicCRSNNPtr GeographicCRS::createEPSG_4807() { auto ellps(datum::Ellipsoid::createFlattenedSphere( createMapNameEPSGCode("Clarke 1880 (IGN)", 7011), common::Length(6378249.2), common::Scale(293.4660212936269))); auto cs(cs::EllipsoidalCS::createLatitudeLongitude( common::UnitOfMeasure::GRAD)); auto datum(datum::GeodeticReferenceFrame::create( createMapNameEPSGCode("Nouvelle Triangulation Francaise (Paris)", 6807), ellps, util::optional(), datum::PrimeMeridian::PARIS)); return create(createMapNameEPSGCode("NTF (Paris)", 4807), datum, cs); } // --------------------------------------------------------------------------- /** \brief Return a variant of this CRS "demoted" to a 2D one, if not already * the case. * * * @param newName Name of the new CRS. If empty, nameStr() will be used. * @param dbContext Database context to look for potentially already registered * 2D CRS. May be nullptr. * @return a new CRS demoted to 2D, or the current one if already 2D or not * applicable. * @since 6.3 */ GeographicCRSNNPtr GeographicCRS::demoteTo2D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const { const auto &axisList = coordinateSystem()->axisList(); if (axisList.size() == 3) { const auto &l_identifiers = identifiers(); // First check if there is a Geographic 2D CRS in the database // of the same name. // This is the common practice in the EPSG dataset. if (dbContext && l_identifiers.size() == 1) { auto authFactory = io::AuthorityFactory::create( NN_NO_CHECK(dbContext), *(l_identifiers[0]->codeSpace())); auto res = authFactory->createObjectsFromName( nameStr(), {io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS}, false); if (!res.empty()) { const auto &firstRes = res.front(); auto firstResAsGeogCRS = util::nn_dynamic_pointer_cast(firstRes); if (firstResAsGeogCRS && firstResAsGeogCRS->is2DPartOf3D(NN_NO_CHECK(this))) { return NN_NO_CHECK(firstResAsGeogCRS); } } } auto cs = cs::EllipsoidalCS::create(util::PropertyMap(), axisList[0], axisList[1]); return GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, !newName.empty() ? newName : nameStr()), datum(), datumEnsemble(), cs); } return NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeographicCRS::addAngularUnitConvertAndAxisSwap( io::PROJStringFormatter *formatter) const { const auto &axisList = coordinateSystem()->axisList(); formatter->addStep("unitconvert"); formatter->addParam("xy_in", "rad"); if (axisList.size() == 3 && !formatter->omitZUnitConversion()) { formatter->addParam("z_in", "m"); } { const auto &unitHoriz = axisList[0]->unit(); const auto projUnit = unitHoriz.exportToPROJString(); if (projUnit.empty()) { formatter->addParam("xy_out", unitHoriz.conversionToSI()); } else { formatter->addParam("xy_out", projUnit); } } if (axisList.size() == 3 && !formatter->omitZUnitConversion()) { const auto &unitZ = axisList[2]->unit(); auto projVUnit = unitZ.exportToPROJString(); if (projVUnit.empty()) { formatter->addParam("z_out", unitZ.conversionToSI()); } else { formatter->addParam("z_out", projVUnit); } } const char *order[2] = {nullptr, nullptr}; const char *one = "1"; const char *two = "2"; for (int i = 0; i < 2; i++) { const auto &dir = axisList[i]->direction(); if (&dir == &cs::AxisDirection::WEST) { order[i] = "-1"; } else if (&dir == &cs::AxisDirection::EAST) { order[i] = one; } else if (&dir == &cs::AxisDirection::SOUTH) { order[i] = "-2"; } else if (&dir == &cs::AxisDirection::NORTH) { order[i] = two; } } if (order[0] && order[1] && (order[0] != one || order[1] != two)) { formatter->addStep("axisswap"); char orderStr[10]; sprintf(orderStr, "%.2s,%.2s", order[0], order[1]); formatter->addParam("order", orderStr); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeographicCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_; if (!extensionProj4.empty()) { formatter->ingestPROJString( replaceAll(extensionProj4, " +type=crs", "")); formatter->addNoDefs(false); return; } if (!formatter->omitProjLongLatIfPossible() || primeMeridian()->longitude().getSIValue() != 0.0 || !formatter->getTOWGS84Parameters().empty() || !formatter->getHDatumExtension().empty()) { formatter->addStep("longlat"); bool done = false; if (formatter->getLegacyCRSToCRSContext() && formatter->getHDatumExtension().empty() && formatter->getTOWGS84Parameters().empty()) { const auto &l_datum = datum(); if (l_datum && l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6326.get(), util::IComparable::Criterion::EQUIVALENT)) { done = true; formatter->addParam("ellps", "WGS84"); } else if (l_datum && l_datum->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6269.get(), util::IComparable::Criterion::EQUIVALENT)) { done = true; // We do not want datum=NAD83 to cause a useless towgs84=0,0,0 formatter->addParam("ellps", "GRS80"); } } if (!done) { addDatumInfoToPROJString(formatter); } } if (!formatter->getCRSExport()) { addAngularUnitConvertAndAxisSwap(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeographicCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("GeographicCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } const auto &l_datum(datum()); if (l_datum) { writer.AddObjKey("datum"); l_datum->_exportToJSON(formatter); } else { writer.AddObjKey("datum_ensemble"); formatter->setOmitTypeInImmediateChild(); datumEnsemble()->_exportToJSON(formatter); } writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct VerticalCRS::Private { std::vector geoidModel{}; std::vector velocityModel{}; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const datum::DatumEnsemblePtr & checkEnsembleForVerticalCRS(const datum::VerticalReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &ensemble) { const char *msg = "One of Datum or DatumEnsemble should be defined"; if (datumIn) { if (!ensemble) { return ensemble; } msg = "Datum and DatumEnsemble should not be defined"; } else if (ensemble) { const auto &datums = ensemble->datums(); assert(!datums.empty()); auto grfFirst = dynamic_cast(datums[0].get()); if (grfFirst) { return ensemble; } msg = "Ensemble should contain VerticalReferenceFrame"; } throw util::Exception(msg); } //! @endcond // --------------------------------------------------------------------------- VerticalCRS::VerticalCRS(const datum::VerticalReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::VerticalCSNNPtr &csIn) : SingleCRS(datumIn, checkEnsembleForVerticalCRS(datumIn, datumEnsembleIn), csIn), d(internal::make_unique()) {} // --------------------------------------------------------------------------- VerticalCRS::VerticalCRS(const VerticalCRS &other) : SingleCRS(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalCRS::~VerticalCRS() = default; //! @endcond // --------------------------------------------------------------------------- CRSNNPtr VerticalCRS::_shallowClone() const { auto crs(VerticalCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the datum::VerticalReferenceFrame associated with the CRS. * * @return a VerticalReferenceFrame. */ const datum::VerticalReferenceFramePtr VerticalCRS::datum() const { return std::static_pointer_cast( SingleCRS::getPrivate()->datum); } // --------------------------------------------------------------------------- /** \brief Return the geoid model associated with the CRS. * * Geoid height model or height correction model linked to a geoid-based * vertical CRS. * * @return a geoid model. might be null */ const std::vector & VerticalCRS::geoidModel() PROJ_PURE_DEFN { return d->geoidModel; } // --------------------------------------------------------------------------- /** \brief Return the velocity model associated with the CRS. * * @return a velocity model. might be null. */ const std::vector & VerticalCRS::velocityModel() PROJ_PURE_DEFN { return d->velocityModel; } // --------------------------------------------------------------------------- /** \brief Return the cs::VerticalCS associated with the CRS. * * @return a VerticalCS. */ const cs::VerticalCSNNPtr VerticalCRS::coordinateSystem() const { return util::nn_static_pointer_cast( SingleCRS::getPrivate()->coordinateSystem); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::VERTCRS : io::WKTConstants::VERT_CS, !identifiers().empty()); formatter->addQuotedString(nameStr()); exportDatumOrDatumEnsembleToWkt(formatter); const auto &cs = SingleCRS::getPrivate()->coordinateSystem; const auto &axisList = cs->axisList(); if (!isWKT2) { axisList[0]->unit()._exportToWKT(formatter); } const auto oldAxisOutputRule = formatter->outputAxis(); if (oldAxisOutputRule == io::WKTFormatter::OutputAxisRule::WKT1_GDAL_EPSG_STYLE) { formatter->setOutputAxis(io::WKTFormatter::OutputAxisRule::YES); } cs->_exportToWKT(formatter); formatter->setOutputAxis(oldAxisOutputRule); if (isWKT2 && formatter->use2019Keywords() && !d->geoidModel.empty()) { const auto &model = d->geoidModel[0]; formatter->startNode(io::WKTConstants::GEOIDMODEL, false); formatter->addQuotedString(model->nameStr()); model->formatID(formatter); formatter->endNode(); } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { auto geoidgrids = formatter->getVDatumExtension(); if (!geoidgrids.empty()) { formatter->addParam("geoidgrids", geoidgrids); } auto &axisList = coordinateSystem()->axisList(); if (!axisList.empty()) { auto projUnit = axisList[0]->unit().exportToPROJString(); if (projUnit.empty()) { formatter->addParam("vto_meter", axisList[0]->unit().conversionToSI()); } else { formatter->addParam("vunits", projUnit); } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("VerticalCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } const auto &l_datum(datum()); if (l_datum) { writer.AddObjKey("datum"); l_datum->_exportToJSON(formatter); } else { writer.AddObjKey("datum_ensemble"); formatter->setOmitTypeInImmediateChild(); datumEnsemble()->_exportToJSON(formatter); } writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); if (!d->geoidModel.empty()) { const auto &model = d->geoidModel[0]; writer.AddObjKey("geoid_model"); auto objectContext2(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("name"); writer.Add(model->nameStr()); if (model->identifiers().empty()) { const auto &interpCRS = model->interpolationCRS(); if (interpCRS) { writer.AddObjKey("interpolation_crs"); interpCRS->_exportToJSON(formatter); } } model->formatID(formatter); } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalCRS::addLinearUnitConvert( io::PROJStringFormatter *formatter) const { auto &axisList = coordinateSystem()->axisList(); if (!axisList.empty()) { if (axisList[0]->unit().conversionToSI() != 1.0) { formatter->addStep("unitconvert"); formatter->addParam("z_in", "m"); auto projVUnit = axisList[0]->unit().exportToPROJString(); if (projVUnit.empty()) { formatter->addParam("z_out", axisList[0]->unit().conversionToSI()); } else { formatter->addParam("z_out", projVUnit); } } } } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalCRS from a datum::VerticalReferenceFrame and a * cs::VerticalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. The GEOID_MODEL property can be set * to a TransformationNNPtr object. * @param datumIn The datum of the CRS. * @param csIn a VerticalCS. * @return new VerticalCRS. */ VerticalCRSNNPtr VerticalCRS::create(const util::PropertyMap &properties, const datum::VerticalReferenceFrameNNPtr &datumIn, const cs::VerticalCSNNPtr &csIn) { return create(properties, datumIn.as_nullable(), nullptr, csIn); } // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalCRS from a datum::VerticalReferenceFrame or * datum::DatumEnsemble and a cs::VerticalCS. * * One and only one of datum or datumEnsemble should be set to a non-null value. * * @param properties See \ref general_properties. * At minimum the name should be defined. The GEOID_MODEL property can be set * to a TransformationNNPtr object. * @param datumIn The datum of the CRS, or nullptr * @param datumEnsembleIn The datum ensemble of the CRS, or nullptr. * @param csIn a VerticalCS. * @return new VerticalCRS. */ VerticalCRSNNPtr VerticalCRS::create(const util::PropertyMap &properties, const datum::VerticalReferenceFramePtr &datumIn, const datum::DatumEnsemblePtr &datumEnsembleIn, const cs::VerticalCSNNPtr &csIn) { auto crs(VerticalCRS::nn_make_shared(datumIn, datumEnsembleIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); const auto geoidModelPtr = properties.get("GEOID_MODEL"); if (geoidModelPtr) { auto transf = util::nn_dynamic_pointer_cast( *geoidModelPtr); if (transf) { crs->d->geoidModel.emplace_back(NN_NO_CHECK(transf)); } } return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool VerticalCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherVertCRS = dynamic_cast(other); // TODO test geoidModel and velocityModel return otherVertCRS != nullptr && SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are looked in the database when * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. * 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * 90% means that CRS are equivalent, but the names are not exactly the same. * 70% means that CRS are equivalent (equivalent datum and coordinate system), * but the names do not match at all. * 25% means that the CRS are not equivalent, but there is some similarity in * the names. * * @param authorityFactory Authority factory (if null, will return an empty * list) * @return a list of matching reference CRS, and the percentage (0-100) of * confidence in the match. */ std::list> VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; const auto &thisName(nameStr()); if (authorityFactory) { const io::DatabaseContextNNPtr &dbContext = authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || ci_equal(thisName, "unnamed"); if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) { // If the CRS has already an id, check in the database for the // official object, and verify that they are equivalent. for (const auto &id : identifiers()) { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( dbContext, *id->codeSpace()) ->createVerticalCRS(id->code()); bool match = _isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { } } } } else if (!unsignificantName) { for (int ipass = 0; ipass < 2; ipass++) { const bool approximateMatch = ipass == 1; auto objects = authorityFactory->createObjectsFromName( thisName, {io::AuthorityFactory::ObjectType::VERTICAL_CRS}, approximateMatch); for (const auto &obj : objects) { auto crs = util::nn_dynamic_pointer_cast(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); if (_isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); return res; } res.emplace_back(crsNN, 90); } else { res.emplace_back(crsNN, 25); } } if (!res.empty()) { break; } } } // Sort results res.sort([&thisName](const Pair &a, const Pair &b) { // First consider confidence if (a.second > b.second) { return true; } if (a.second < b.second) { return false; } // Then consider exact name matching const auto &aName(a.first->nameStr()); const auto &bName(b.first->nameStr()); if (aName == thisName && bName != thisName) { return true; } if (bName == thisName && aName != thisName) { return false; } // Arbitrary final sorting criterion return aName < bName; }); // Keep only results of the highest confidence if (res.size() >= 2) { const auto highestConfidence = res.front().second; std::list newRes; for (const auto &pair : res) { if (pair.second == highestConfidence) { newRes.push_back(pair); } else { break; } } return newRes; } } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> VerticalCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; auto resTemp = identify(authorityFactory); for (const auto &pair : resTemp) { res.emplace_back(pair.first, pair.second); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DerivedCRS::Private { SingleCRSNNPtr baseCRS_; operation::ConversionNNPtr derivingConversion_; Private(const SingleCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn) : baseCRS_(baseCRSIn), derivingConversion_(derivingConversionIn) {} // For the conversion make a _shallowClone(), so that we can later set // its targetCRS to this. Private(const Private &other) : baseCRS_(other.baseCRS_), derivingConversion_(other.derivingConversion_->shallowClone()) {} }; //! @endcond // --------------------------------------------------------------------------- // DerivedCRS is an abstract class, that virtually inherits from SingleCRS // Consequently the base constructor in SingleCRS will never be called by // that constructor. clang -Wabstract-vbase-init and VC++ underline this, but // other // compilers will complain if we don't call the base constructor. DerivedCRS::DerivedCRS(const SingleCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CoordinateSystemNNPtr & #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT) cs #endif ) : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT) SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), cs), #endif d(internal::make_unique(baseCRSIn, derivingConversionIn)) { } // --------------------------------------------------------------------------- DerivedCRS::DerivedCRS(const DerivedCRS &other) : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT) SingleCRS(other), #endif d(internal::make_unique(*other.d)) { } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DerivedCRS::~DerivedCRS() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the base CRS of a DerivedCRS. * * @return the base CRS. */ const SingleCRSNNPtr &DerivedCRS::baseCRS() PROJ_PURE_DEFN { return d->baseCRS_; } // --------------------------------------------------------------------------- /** \brief Return the deriving conversion from the base CRS to this CRS. * * @return the deriving conversion. */ const operation::ConversionNNPtr DerivedCRS::derivingConversion() const { return d->derivingConversion_->shallowClone(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const operation::ConversionNNPtr & DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN { return d->derivingConversion_; } //! @endcond // --------------------------------------------------------------------------- bool DerivedCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); const auto standardCriterion = getStandardCriterion(criterion); if (otherDerivedCRS == nullptr || !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) { return false; } return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(), criterion, dbContext) && d->derivingConversion_->_isEquivalentTo( otherDerivedCRS->d->derivingConversion_.get(), standardCriterion, dbContext); } // --------------------------------------------------------------------------- void DerivedCRS::setDerivingConversionCRS() { derivingConversionRef()->setWeakSourceTargetCRS( baseCRS().as_nullable(), std::static_pointer_cast(shared_from_this().as_nullable())); } // --------------------------------------------------------------------------- void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter, const std::string &keyword, const std::string &baseKeyword) const { formatter->startNode(keyword, !identifiers().empty()); formatter->addQuotedString(nameStr()); const auto &l_baseCRS = d->baseCRS_; formatter->startNode(baseKeyword, formatter->use2019Keywords() && !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); if (formatter->use2019Keywords() && !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) { l_baseCRS->formatID(formatter); } formatter->endNode(); formatter->setUseDerivingConversion(true); derivingConversionRef()->_exportToWKT(formatter); formatter->setUseDerivingConversion(false); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DerivedCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext(className(), !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("base_crs"); baseCRS()->_exportToJSON(formatter); writer.AddObjKey("conversion"); formatter->setOmitTypeInImmediateChild(); derivingConversionRef()->_exportToJSON(formatter); writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ProjectedCRS::Private { GeodeticCRSNNPtr baseCRS_; cs::CartesianCSNNPtr cs_; Private(const GeodeticCRSNNPtr &baseCRSIn, const cs::CartesianCSNNPtr &csIn) : baseCRS_(baseCRSIn), cs_(csIn) {} inline const GeodeticCRSNNPtr &baseCRS() const { return baseCRS_; } inline const cs::CartesianCSNNPtr &coordinateSystem() const { return cs_; } }; //! @endcond // --------------------------------------------------------------------------- ProjectedCRS::ProjectedCRS( const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CartesianCSNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(internal::make_unique(baseCRSIn, csIn)) {} // --------------------------------------------------------------------------- ProjectedCRS::ProjectedCRS(const ProjectedCRS &other) : SingleCRS(other), DerivedCRS(other), d(internal::make_unique(other.baseCRS(), other.coordinateSystem())) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ProjectedCRS::~ProjectedCRS() = default; //! @endcond // --------------------------------------------------------------------------- CRSNNPtr ProjectedCRS::_shallowClone() const { auto crs(ProjectedCRS::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Return the base CRS (a GeodeticCRS, which is generally a * GeographicCRS) of the ProjectedCRS. * * @return the base CRS. */ const GeodeticCRSNNPtr &ProjectedCRS::baseCRS() PROJ_PURE_DEFN { return d->baseCRS(); } // --------------------------------------------------------------------------- /** \brief Return the cs::CartesianCS associated with the CRS. * * @return a CartesianCS */ const cs::CartesianCSNNPtr &ProjectedCRS::coordinateSystem() PROJ_PURE_DEFN { return d->coordinateSystem(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const auto &l_identifiers = identifiers(); // Try to perfectly round-trip ESRI projectedCRS if the current object // perfectly matches the database definition const auto &dbContext = formatter->databaseContext(); auto l_name = nameStr(); std::string l_alias; if (formatter->useESRIDialect() && dbContext) { l_alias = dbContext->getAliasFromOfficialName(l_name, "projected_crs", "ESRI"); } if (!isWKT2 && formatter->useESRIDialect() && !l_identifiers.empty() && *(l_identifiers[0]->codeSpace()) == "ESRI" && dbContext) { try { const auto definition = dbContext->getTextDefinition( "projected_crs", "ESRI", l_identifiers[0]->code()); if (starts_with(definition, "PROJCS")) { auto crsFromFromDef = io::WKTParser() .attachDatabaseContext(dbContext) .createFromWKT(definition); if (_isEquivalentTo( dynamic_cast(crsFromFromDef.get()), util::IComparable::Criterion::EQUIVALENT)) { formatter->ingestWKTNode( io::WKTNode::createFrom(definition)); return; } } } catch (const std::exception &) { } } else if (!isWKT2 && formatter->useESRIDialect() && !l_alias.empty()) { try { auto res = io::AuthorityFactory::create(NN_NO_CHECK(dbContext), "ESRI") ->createObjectsFromName( l_alias, {io::AuthorityFactory::ObjectType::PROJECTED_CRS}, false); if (res.size() == 1) { const auto definition = dbContext->getTextDefinition( "projected_crs", "ESRI", res.front()->identifiers()[0]->code()); if (starts_with(definition, "PROJCS")) { if (_isEquivalentTo( dynamic_cast(res.front().get()), util::IComparable::Criterion::EQUIVALENT)) { formatter->ingestWKTNode( io::WKTNode::createFrom(definition)); return; } } } } catch (const std::exception &) { } } const auto &l_coordinateSystem = d->coordinateSystem(); const auto &axisList = l_coordinateSystem->axisList(); if (axisList.size() == 3 && !(isWKT2 && formatter->use2019Keywords())) { io::FormattingException::Throw( "Projected 3D CRS can only be exported since WKT2:2019"); } const auto exportAxis = [&l_coordinateSystem, &axisList, &formatter]() { const auto oldAxisOutputRule = formatter->outputAxis(); if (oldAxisOutputRule == io::WKTFormatter::OutputAxisRule::WKT1_GDAL_EPSG_STYLE) { if (&axisList[0]->direction() == &cs::AxisDirection::EAST && &axisList[1]->direction() == &cs::AxisDirection::NORTH) { formatter->setOutputAxis(io::WKTFormatter::OutputAxisRule::YES); } } l_coordinateSystem->_exportToWKT(formatter); formatter->setOutputAxis(oldAxisOutputRule); }; if (!isWKT2 && !formatter->useESRIDialect() && starts_with(nameStr(), "Popular Visualisation CRS / Mercator")) { formatter->startNode(io::WKTConstants::PROJCS, !l_identifiers.empty()); formatter->addQuotedString(nameStr()); formatter->setTOWGS84Parameters({0, 0, 0, 0, 0, 0, 0}); baseCRS()->_exportToWKT(formatter); formatter->setTOWGS84Parameters({}); formatter->startNode(io::WKTConstants::PROJECTION, false); formatter->addQuotedString("Mercator_1SP"); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("central_meridian"); formatter->add(0.0); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("scale_factor"); formatter->add(1.0); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("false_easting"); formatter->add(0.0); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("false_northing"); formatter->add(0.0); formatter->endNode(); axisList[0]->unit()._exportToWKT(formatter); exportAxis(); derivingConversionRef()->addWKTExtensionNode(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); return; } formatter->startNode(isWKT2 ? io::WKTConstants::PROJCRS : io::WKTConstants::PROJCS, !l_identifiers.empty()); if (formatter->useESRIDialect()) { if (l_alias.empty()) { l_name = io::WKTFormatter::morphNameToESRI(l_name); } else { l_name = l_alias; } } if (!isWKT2 && !formatter->useESRIDialect() && isDeprecated()) { l_name += " (deprecated)"; } formatter->addQuotedString(l_name); const auto &l_baseCRS = d->baseCRS(); const auto &geodeticCRSAxisList = l_baseCRS->coordinateSystem()->axisList(); if (isWKT2) { formatter->startNode( (formatter->use2019Keywords() && dynamic_cast(l_baseCRS.get())) ? io::WKTConstants::BASEGEOGCRS : io::WKTConstants::BASEGEODCRS, formatter->use2019Keywords() && !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); // insert ellipsoidal cs unit when the units of the map // projection angular parameters are not explicitly given within those // parameters. See // http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#61 if (formatter->primeMeridianOrParameterUnitOmittedIfSameAsAxis()) { geodeticCRSAxisList[0]->unit()._exportToWKT(formatter); } l_baseCRS->primeMeridian()->_exportToWKT(formatter); if (formatter->use2019Keywords() && !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) { l_baseCRS->formatID(formatter); } formatter->endNode(); } else { const auto oldAxisOutputRule = formatter->outputAxis(); formatter->setOutputAxis(io::WKTFormatter::OutputAxisRule::NO); l_baseCRS->_exportToWKT(formatter); formatter->setOutputAxis(oldAxisOutputRule); } formatter->pushAxisLinearUnit( common::UnitOfMeasure::create(axisList[0]->unit())); formatter->pushAxisAngularUnit( common::UnitOfMeasure::create(geodeticCRSAxisList[0]->unit())); derivingConversionRef()->_exportToWKT(formatter); formatter->popAxisAngularUnit(); formatter->popAxisLinearUnit(); if (!isWKT2) { axisList[0]->unit()._exportToWKT(formatter); } exportAxis(); if (!isWKT2 && !formatter->useESRIDialect()) { const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_; if (!extensionProj4.empty()) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); formatter->addQuotedString(extensionProj4); formatter->endNode(); } else { derivingConversionRef()->addWKTExtensionNode(formatter); } } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); return; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ProjectedCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("ProjectedCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("base_crs"); formatter->setAllowIDInImmediateChild(); formatter->setOmitTypeInImmediateChild(); baseCRS()->_exportToJSON(formatter); writer.AddObjKey("conversion"); formatter->setOmitTypeInImmediateChild(); derivingConversionRef()->_exportToJSON(formatter); writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- void ProjectedCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { const auto &extensionProj4 = CRS::getPrivate()->extensionProj4_; if (!extensionProj4.empty()) { formatter->ingestPROJString( replaceAll(extensionProj4, " +type=crs", "")); formatter->addNoDefs(false); return; } derivingConversionRef()->_exportToPROJString(formatter); } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS from a base CRS, a deriving * operation::Conversion * and a coordinate system. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn The base CRS, a GeodeticCRS that is generally a * GeographicCRS. * @param derivingConversionIn The deriving operation::Conversion (typically * using a map * projection method) * @param csIn The coordniate system. * @return new ProjectedCRS. */ ProjectedCRSNNPtr ProjectedCRS::create(const util::PropertyMap &properties, const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CartesianCSNNPtr &csIn) { auto crs = ProjectedCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); properties.getStringValue("EXTENSION_PROJ4", crs->CRS::getPrivate()->extensionProj4_); crs->CRS::getPrivate()->setImplicitCS(properties); return crs; } // --------------------------------------------------------------------------- bool ProjectedCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherProjCRS = dynamic_cast(other); return otherProjCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ProjectedCRSNNPtr ProjectedCRS::alterParametersLinearUnit(const common::UnitOfMeasure &unit, bool convertToNewUnit) const { return create( createPropertyMap(this), baseCRS(), derivingConversion()->alterParametersLinearUnit(unit, convertToNewUnit), coordinateSystem()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter, bool axisSpecFound) const { const auto &axisList = d->coordinateSystem()->axisList(); const auto &unit = axisList[0]->unit(); const auto *zUnit = axisList.size() == 3 ? &(axisList[2]->unit()) : nullptr; if (!unit._isEquivalentTo(common::UnitOfMeasure::METRE, util::IComparable::Criterion::EQUIVALENT) || (zUnit && !zUnit->_isEquivalentTo(common::UnitOfMeasure::METRE, util::IComparable::Criterion::EQUIVALENT))) { auto projUnit = unit.exportToPROJString(); const double toSI = unit.conversionToSI(); if (!formatter->getCRSExport()) { formatter->addStep("unitconvert"); formatter->addParam("xy_in", "m"); if (zUnit) formatter->addParam("z_in", "m"); if (projUnit.empty()) { formatter->addParam("xy_out", toSI); } else { formatter->addParam("xy_out", projUnit); } if (zUnit) { auto projZUnit = zUnit->exportToPROJString(); const double zToSI = zUnit->conversionToSI(); if (projZUnit.empty()) { formatter->addParam("z_out", zToSI); } else { formatter->addParam("z_out", projZUnit); } } } else { if (projUnit.empty()) { formatter->addParam("to_meter", toSI); } else { formatter->addParam("units", projUnit); } } } else if (formatter->getCRSExport() && !formatter->getLegacyCRSToCRSContext()) { formatter->addParam("units", "m"); } if (!axisSpecFound && !formatter->getCRSExport()) { const auto &dir0 = axisList[0]->direction(); const auto &dir1 = axisList[1]->direction(); if (!(&dir0 == &cs::AxisDirection::EAST && &dir1 == &cs::AxisDirection::NORTH) && // For polar projections, that have south+south direction, // we don't want to mess with axes. dir0 != dir1) { const char *order[2] = {nullptr, nullptr}; for (int i = 0; i < 2; i++) { const auto &dir = axisList[i]->direction(); if (&dir == &cs::AxisDirection::WEST) order[i] = "-1"; else if (&dir == &cs::AxisDirection::EAST) order[i] = "1"; else if (&dir == &cs::AxisDirection::SOUTH) order[i] = "-2"; else if (&dir == &cs::AxisDirection::NORTH) order[i] = "2"; } if (order[0] && order[1]) { formatter->addStep("axisswap"); char orderStr[10]; sprintf(orderStr, "%.2s,%.2s", order[0], order[1]); formatter->addParam("order", orderStr); } } else { const auto &name0 = axisList[0]->nameStr(); const auto &name1 = axisList[1]->nameStr(); const bool northingEasting = ci_starts_with(name0, "northing") && ci_starts_with(name1, "easting"); // case of EPSG:32661 ["WGS 84 / UPS North (N,E)]" // case of EPSG:32761 ["WGS 84 / UPS South (N,E)]" if (((&dir0 == &cs::AxisDirection::SOUTH && &dir1 == &cs::AxisDirection::SOUTH) || (&dir0 == &cs::AxisDirection::NORTH && &dir1 == &cs::AxisDirection::NORTH)) && northingEasting) { formatter->addStep("axisswap"); formatter->addParam("order", "2,1"); } } } } //! @endcond // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are either hard-coded, or looked in the database when * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. * * 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * 90% means that CRS are equivalent, but the names are not exactly the same. * 70% means that CRS are equivalent (equivalent base CRS, conversion and * coordinate system), but the names do not match at all. * 50% means that CRS have similarity (equivalent base CRS and conversion), * but the coordinate system do not match (e.g. different axis ordering or * axis unit). * 25% means that the CRS are not equivalent, but there is some similarity in * the names. * * For the purpose of this function, equivalence is tested with the * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, that is * to say that the axis order of the base GeographicCRS is ignored. * * @param authorityFactory Authority factory (or null, but degraded * functionality) * @return a list of matching reference CRS, and the percentage (0-100) of * confidence in the match. */ std::list> ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; const auto &thisName(nameStr()); std::list> baseRes; const auto &l_baseCRS(baseCRS()); auto geogCRS = dynamic_cast(l_baseCRS.get()); if (geogCRS && geogCRS->coordinateSystem()->axisOrder() == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH) { baseRes = GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, geogCRS->nameStr()), geogCRS->datum(), geogCRS->datumEnsemble(), cs::EllipsoidalCS::createLatitudeLongitude( geogCRS->coordinateSystem()->axisList()[0]->unit())) ->identify(authorityFactory); } else { baseRes = l_baseCRS->identify(authorityFactory); } int zone = 0; bool north = false; auto computeConfidence = [&thisName](const std::string &crsName) { return crsName == thisName ? 100 : metadata::Identifier::isEquivalentName( crsName.c_str(), thisName.c_str()) ? 90 : 70; }; auto computeUTMCRSName = [](const char *base, int l_zone, bool l_north) { return base + toString(l_zone) + (l_north ? "N" : "S"); }; const auto &conv = derivingConversionRef(); const auto &cs = coordinateSystem(); io::DatabaseContextPtr dbContext = authorityFactory ? authorityFactory->databaseContext().as_nullable() : nullptr; if (baseRes.size() == 1 && baseRes.front().second >= 70 && conv->isUTM(zone, north) && cs->_isEquivalentTo( cs::CartesianCS::createEastingNorthing(common::UnitOfMeasure::METRE) .get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4326.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("WGS 84 / UTM zone ", zone, north)); res.emplace_back( ProjectedCRS::create( createMapNameEPSGCode(crsName.c_str(), (north ? 32600 : 32700) + zone), GeographicCRS::EPSG_4326, conv->identify(), cs), computeConfidence(crsName)); return res; } else if (((zone >= 1 && zone <= 22) || zone == 59 || zone == 60) && north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4267.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD27 / UTM zone ", zone, north)); res.emplace_back( ProjectedCRS::create( createMapNameEPSGCode(crsName.c_str(), (zone >= 59) ? 3370 + zone - 59 : 26700 + zone), GeographicCRS::EPSG_4267, conv->identify(), cs), computeConfidence(crsName)); return res; } else if (((zone >= 1 && zone <= 23) || zone == 59 || zone == 60) && north && baseRes.front().first->_isEquivalentTo( GeographicCRS::EPSG_4269.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { std::string crsName( computeUTMCRSName("NAD83 / UTM zone ", zone, north)); res.emplace_back( ProjectedCRS::create( createMapNameEPSGCode(crsName.c_str(), (zone >= 59) ? 3372 + zone - 59 : 26900 + zone), GeographicCRS::EPSG_4269, conv->identify(), cs), computeConfidence(crsName)); return res; } } if (authorityFactory) { const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || ci_equal(thisName, "unnamed"); bool foundEquivalentName = false; if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) { // If the CRS has already an id, check in the database for the // official object, and verify that they are equivalent. for (const auto &id : identifiers()) { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( authorityFactory->databaseContext(), *id->codeSpace()) ->createProjectedCRS(id->code()); bool match = _isEquivalentTo( crs.get(), util::IComparable::Criterion:: EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { } } } } else if (!unsignificantName) { for (int ipass = 0; ipass < 2; ipass++) { const bool approximateMatch = ipass == 1; auto objects = authorityFactory->createObjectsFromName( thisName, {io::AuthorityFactory::ObjectType::PROJECTED_CRS}, approximateMatch); for (const auto &obj : objects) { auto crs = util::nn_dynamic_pointer_cast(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); const bool eqName = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( crs.get(), util::IComparable::Criterion:: EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); return res; } res.emplace_back(crsNN, eqName ? 90 : 70); } else if (crs->nameStr() == thisName && CRS::getPrivate()->implicitCS_ && coordinateSystem() ->axisList()[0] ->unit() ._isEquivalentTo( crs->coordinateSystem() ->axisList()[0] ->unit(), util::IComparable::Criterion:: EQUIVALENT) && l_baseCRS->_isEquivalentTo( crs->baseCRS().get(), util::IComparable::Criterion:: EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), util::IComparable::Criterion::EQUIVALENT, dbContext) && objects.size() == 1) { res.clear(); res.emplace_back(crsNN, 100); return res; } else { res.emplace_back(crsNN, 25); } } if (!res.empty()) { break; } } } const auto lambdaSort = [&thisName](const Pair &a, const Pair &b) { // First consider confidence if (a.second > b.second) { return true; } if (a.second < b.second) { return false; } // Then consider exact name matching const auto &aName(a.first->nameStr()); const auto &bName(b.first->nameStr()); if (aName == thisName && bName != thisName) { return true; } if (bName == thisName && aName != thisName) { return false; } // Arbitrary final sorting criterion return aName < bName; }; // Sort results res.sort(lambdaSort); if (!hasCodeCompatibleOfAuthorityFactory(this, authorityFactory) && !foundEquivalentName && (res.empty() || res.front().second < 50)) { std::set> alreadyKnown; for (const auto &pair : res) { const auto &ids = pair.first->identifiers(); assert(!ids.empty()); alreadyKnown.insert(std::pair( *(ids[0]->codeSpace()), ids[0]->code())); } auto self = NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); auto candidates = authorityFactory->createProjectedCRSFromExisting(self); const auto &ellipsoid = l_baseCRS->ellipsoid(); for (const auto &crs : candidates) { const auto &ids = crs->identifiers(); assert(!ids.empty()); if (alreadyKnown.find(std::pair( *(ids[0]->codeSpace()), ids[0]->code())) != alreadyKnown.end()) { continue; } if (_isEquivalentTo(crs.get(), util::IComparable::Criterion:: EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS, dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else if (ellipsoid->_isEquivalentTo( crs->baseCRS()->ellipsoid().get(), util::IComparable::Criterion::EQUIVALENT, dbContext) && derivingConversionRef()->_isEquivalentTo( crs->derivingConversionRef().get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (coordinateSystem()->_isEquivalentTo( crs->coordinateSystem().get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { res.emplace_back(crs, 70); } else { res.emplace_back(crs, 50); } } else { res.emplace_back(crs, 25); } } res.sort(lambdaSort); } // Keep only results of the highest confidence if (res.size() >= 2) { const auto highestConfidence = res.front().second; std::list newRes; for (const auto &pair : res) { if (pair.second == highestConfidence) { newRes.push_back(pair); } else { break; } } return newRes; } } return res; } // --------------------------------------------------------------------------- /** \brief Return a variant of this CRS "demoted" to a 2D one, if not already * the case. * * * @param newName Name of the new CRS. If empty, nameStr() will be used. * @param dbContext Database context to look for potentially already registered * 2D CRS. May be nullptr. * @return a new CRS demoted to 2D, or the current one if already 2D or not * applicable. * @since 6.3 */ ProjectedCRSNNPtr ProjectedCRS::demoteTo2D(const std::string &newName, const io::DatabaseContextPtr &dbContext) const { const auto &axisList = coordinateSystem()->axisList(); if (axisList.size() == 3) { auto cs = cs::CartesianCS::create(util::PropertyMap(), axisList[0], axisList[1]); const auto &l_baseCRS = baseCRS(); const auto geogCRS = dynamic_cast(l_baseCRS.get()); const auto newBaseCRS = geogCRS ? util::nn_static_pointer_cast( geogCRS->demoteTo2D(std::string(), dbContext)) : l_baseCRS; return ProjectedCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, !newName.empty() ? newName : nameStr()), newBaseCRS, derivingConversion(), cs); } return NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> ProjectedCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; auto resTemp = identify(authorityFactory); for (const auto &pair : resTemp) { res.emplace_back(pair.first, pair.second); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CompoundCRS::Private { std::vector components_{}; }; //! @endcond // --------------------------------------------------------------------------- CompoundCRS::CompoundCRS(const std::vector &components) : CRS(), d(internal::make_unique()) { d->components_ = components; } // --------------------------------------------------------------------------- CompoundCRS::CompoundCRS(const CompoundCRS &other) : CRS(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CompoundCRS::~CompoundCRS() = default; //! @endcond // --------------------------------------------------------------------------- CRSNNPtr CompoundCRS::_shallowClone() const { auto crs(CompoundCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the components of a CompoundCRS. * * @return the components. */ const std::vector & CompoundCRS::componentReferenceSystems() PROJ_PURE_DEFN { return d->components_; } // --------------------------------------------------------------------------- /** \brief Instantiate a CompoundCRS from a vector of CRS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param components the component CRS of the CompoundCRS. * @return new CompoundCRS. */ CompoundCRSNNPtr CompoundCRS::create(const util::PropertyMap &properties, const std::vector &components) { auto compoundCRS(CompoundCRS::nn_make_shared(components)); compoundCRS->assignSelf(compoundCRS); compoundCRS->setProperties(properties); if (!properties.get(common::IdentifiedObject::NAME_KEY)) { std::string name; for (const auto &crs : components) { if (!name.empty()) { name += " + "; } const auto &l_name = crs->nameStr(); if (!l_name.empty()) { name += l_name; } else { name += "unnamed"; } } util::PropertyMap propertyName; propertyName.set(common::IdentifiedObject::NAME_KEY, name); compoundCRS->setProperties(propertyName); } return compoundCRS; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CompoundCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::COMPOUNDCRS : io::WKTConstants::COMPD_CS, !identifiers().empty()); formatter->addQuotedString(nameStr()); for (const auto &crs : componentReferenceSystems()) { crs->_exportToWKT(formatter); } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CompoundCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("CompoundCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("components"); { auto componentsContext(writer.MakeArrayContext(false)); for (const auto &crs : componentReferenceSystems()) { crs->_exportToJSON(formatter); } } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- void CompoundCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { for (const auto &crs : componentReferenceSystems()) { auto crs_exportable = dynamic_cast(crs.get()); if (crs_exportable) { crs_exportable->_exportToPROJString(formatter); } } } // --------------------------------------------------------------------------- bool CompoundCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherCompoundCRS = dynamic_cast(other); if (otherCompoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &components = componentReferenceSystems(); const auto &otherComponents = otherCompoundCRS->componentReferenceSystems(); if (components.size() != otherComponents.size()) { return false; } for (size_t i = 0; i < components.size(); i++) { if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion, dbContext)) { return false; } } return true; } // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are looked in the database when * authorityFactory is not null. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. * * 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * 90% means that CRS are equivalent, but the names are not exactly the same. * 70% means that CRS are equivalent (equivalent horizontal and vertical CRS), * but the names do not match at all. * 25% means that the CRS are not equivalent, but there is some similarity in * the names. * * @param authorityFactory Authority factory (if null, will return an empty * list) * @return a list of matching reference CRS, and the percentage (0-100) of * confidence in the match. */ std::list> CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; const auto &thisName(nameStr()); if (authorityFactory) { const io::DatabaseContextNNPtr &dbContext = authorityFactory->databaseContext(); const bool unsignificantName = thisName.empty() || ci_equal(thisName, "unknown") || ci_equal(thisName, "unnamed"); bool foundEquivalentName = false; if (hasCodeCompatibleOfAuthorityFactory(this, authorityFactory)) { // If the CRS has already an id, check in the database for the // official object, and verify that they are equivalent. for (const auto &id : identifiers()) { if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) { try { auto crs = io::AuthorityFactory::create( dbContext, *id->codeSpace()) ->createCompoundCRS(id->code()); bool match = _isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT, dbContext); res.emplace_back(crs, match ? 100 : 25); return res; } catch (const std::exception &) { } } } } else if (!unsignificantName) { for (int ipass = 0; ipass < 2; ipass++) { const bool approximateMatch = ipass == 1; auto objects = authorityFactory->createObjectsFromName( thisName, {io::AuthorityFactory::ObjectType::COMPOUND_CRS}, approximateMatch); for (const auto &obj : objects) { auto crs = util::nn_dynamic_pointer_cast(obj); assert(crs); auto crsNN = NN_NO_CHECK(crs); const bool eqName = metadata::Identifier::isEquivalentName( thisName.c_str(), crs->nameStr().c_str()); foundEquivalentName |= eqName; if (_isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { if (crs->nameStr() == thisName) { res.clear(); res.emplace_back(crsNN, 100); return res; } res.emplace_back(crsNN, eqName ? 90 : 70); } else { res.emplace_back(crsNN, 25); } } if (!res.empty()) { break; } } } const auto lambdaSort = [&thisName](const Pair &a, const Pair &b) { // First consider confidence if (a.second > b.second) { return true; } if (a.second < b.second) { return false; } // Then consider exact name matching const auto &aName(a.first->nameStr()); const auto &bName(b.first->nameStr()); if (aName == thisName && bName != thisName) { return true; } if (bName == thisName && aName != thisName) { return false; } // Arbitrary final sorting criterion return aName < bName; }; // Sort results res.sort(lambdaSort); if (identifiers().empty() && !foundEquivalentName && (res.empty() || res.front().second < 50)) { std::set> alreadyKnown; for (const auto &pair : res) { const auto &ids = pair.first->identifiers(); assert(!ids.empty()); alreadyKnown.insert(std::pair( *(ids[0]->codeSpace()), ids[0]->code())); } auto self = NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); auto candidates = authorityFactory->createCompoundCRSFromExisting(self); for (const auto &crs : candidates) { const auto &ids = crs->identifiers(); assert(!ids.empty()); if (alreadyKnown.find(std::pair( *(ids[0]->codeSpace()), ids[0]->code())) != alreadyKnown.end()) { continue; } if (_isEquivalentTo(crs.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { res.emplace_back(crs, unsignificantName ? 90 : 70); } else { res.emplace_back(crs, 25); } } res.sort(lambdaSort); } // Keep only results of the highest confidence if (res.size() >= 2) { const auto highestConfidence = res.front().second; std::list newRes; for (const auto &pair : res) { if (pair.second == highestConfidence) { newRes.push_back(pair); } else { break; } } return newRes; } } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> CompoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; auto resTemp = identify(authorityFactory); for (const auto &pair : resTemp) { res.emplace_back(pair.first, pair.second); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct PROJ_INTERNAL BoundCRS::Private { CRSNNPtr baseCRS_; CRSNNPtr hubCRS_; operation::TransformationNNPtr transformation_; Private(const CRSNNPtr &baseCRSIn, const CRSNNPtr &hubCRSIn, const operation::TransformationNNPtr &transformationIn); inline const CRSNNPtr &baseCRS() const { return baseCRS_; } inline const CRSNNPtr &hubCRS() const { return hubCRS_; } inline const operation::TransformationNNPtr &transformation() const { return transformation_; } }; BoundCRS::Private::Private( const CRSNNPtr &baseCRSIn, const CRSNNPtr &hubCRSIn, const operation::TransformationNNPtr &transformationIn) : baseCRS_(baseCRSIn), hubCRS_(hubCRSIn), transformation_(transformationIn) {} //! @endcond // --------------------------------------------------------------------------- BoundCRS::BoundCRS(const CRSNNPtr &baseCRSIn, const CRSNNPtr &hubCRSIn, const operation::TransformationNNPtr &transformationIn) : d(internal::make_unique(baseCRSIn, hubCRSIn, transformationIn)) { } // --------------------------------------------------------------------------- BoundCRS::BoundCRS(const BoundCRS &other) : CRS(other), d(internal::make_unique(other.d->baseCRS(), other.d->hubCRS(), other.d->transformation())) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress BoundCRS::~BoundCRS() = default; //! @endcond // --------------------------------------------------------------------------- BoundCRSNNPtr BoundCRS::shallowCloneAsBoundCRS() const { auto crs(BoundCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- CRSNNPtr BoundCRS::_shallowClone() const { return shallowCloneAsBoundCRS(); } // --------------------------------------------------------------------------- /** \brief Return the base CRS. * * This is the CRS into which coordinates of the BoundCRS are expressed. * * @return the base CRS. */ const CRSNNPtr &BoundCRS::baseCRS() PROJ_PURE_DEFN { return d->baseCRS_; } // --------------------------------------------------------------------------- // The only legit caller is BoundCRS::baseCRSWithCanonicalBoundCRS() void CRS::setCanonicalBoundCRS(const BoundCRSNNPtr &boundCRS) { d->canonicalBoundCRS_ = boundCRS; } // --------------------------------------------------------------------------- /** \brief Return a shallow clone of the base CRS that points to a * shallow clone of this BoundCRS. * * The base CRS is the CRS into which coordinates of the BoundCRS are expressed. * * The returned CRS will actually be a shallow clone of the actual base CRS, * with the extra property that CRS::canonicalBoundCRS() will point to a * shallow clone of this BoundCRS. Use this only if you want to work with * the base CRS object rather than the BoundCRS, but wanting to be able to * retrieve the BoundCRS later. * * @return the base CRS. */ CRSNNPtr BoundCRS::baseCRSWithCanonicalBoundCRS() const { auto baseCRSClone = baseCRS()->_shallowClone(); baseCRSClone->setCanonicalBoundCRS(shallowCloneAsBoundCRS()); return baseCRSClone; } // --------------------------------------------------------------------------- /** \brief Return the target / hub CRS. * * @return the hub CRS. */ const CRSNNPtr &BoundCRS::hubCRS() PROJ_PURE_DEFN { return d->hubCRS_; } // --------------------------------------------------------------------------- /** \brief Return the transformation to the hub RS. * * @return transformation. */ const operation::TransformationNNPtr & BoundCRS::transformation() PROJ_PURE_DEFN { return d->transformation_; } // --------------------------------------------------------------------------- /** \brief Instantiate a BoundCRS from a base CRS, a hub CRS and a * transformation. * * @param baseCRSIn base CRS. * @param hubCRSIn hub CRS. * @param transformationIn transformation from base CRS to hub CRS. * @return new BoundCRS. */ BoundCRSNNPtr BoundCRS::create(const CRSNNPtr &baseCRSIn, const CRSNNPtr &hubCRSIn, const operation::TransformationNNPtr &transformationIn) { auto crs = BoundCRS::nn_make_shared(baseCRSIn, hubCRSIn, transformationIn); crs->assignSelf(crs); const auto &l_name = baseCRSIn->nameStr(); if (!l_name.empty()) { crs->setProperties(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, l_name)); } return crs; } // --------------------------------------------------------------------------- /** \brief Instantiate a BoundCRS from a base CRS and TOWGS84 parameters * * @param baseCRSIn base CRS. * @param TOWGS84Parameters a vector of 3 or 7 double values representing WKT1 * TOWGS84 parameter. * @return new BoundCRS. */ BoundCRSNNPtr BoundCRS::createFromTOWGS84(const CRSNNPtr &baseCRSIn, const std::vector &TOWGS84Parameters) { auto geodCRS = baseCRSIn->extractGeodeticCRS(); auto targetCRS = geodCRS.get() == nullptr || dynamic_cast(geodCRS.get()) ? util::nn_static_pointer_cast( crs::GeographicCRS::EPSG_4326) : util::nn_static_pointer_cast( crs::GeodeticCRS::EPSG_4978); return create( baseCRSIn, targetCRS, operation::Transformation::createTOWGS84(baseCRSIn, TOWGS84Parameters)); } // --------------------------------------------------------------------------- /** \brief Instantiate a BoundCRS from a base CRS and nadgrids parameters * * @param baseCRSIn base CRS. * @param filename Horizontal grid filename * @return new BoundCRS. */ BoundCRSNNPtr BoundCRS::createFromNadgrids(const CRSNNPtr &baseCRSIn, const std::string &filename) { const CRSPtr sourceGeographicCRS = baseCRSIn->extractGeographicCRS(); auto transformationSourceCRS = sourceGeographicCRS ? sourceGeographicCRS : baseCRSIn.as_nullable(); std::string transformationName = transformationSourceCRS->nameStr(); transformationName += " to WGS84"; return create( baseCRSIn, GeographicCRS::EPSG_4326, operation::Transformation::createNTv2( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, transformationName), NN_NO_CHECK(transformationSourceCRS), GeographicCRS::EPSG_4326, filename, std::vector())); } // --------------------------------------------------------------------------- bool BoundCRS::isTOWGS84Compatible() const { return dynamic_cast(d->hubCRS().get()) != nullptr && ci_equal(d->hubCRS()->nameStr(), "WGS 84"); } // --------------------------------------------------------------------------- std::string BoundCRS::getHDatumPROJ4GRIDS() const { if (ci_equal(d->hubCRS()->nameStr(), "WGS 84")) { return d->transformation()->getNTv2Filename(); } return std::string(); } // --------------------------------------------------------------------------- std::string BoundCRS::getVDatumPROJ4GRIDS() const { if (dynamic_cast(d->baseCRS().get()) && ci_equal(d->hubCRS()->nameStr(), "WGS 84")) { return d->transformation()->getHeightToGeographic3DFilename(); } return std::string(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void BoundCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (isWKT2) { formatter->startNode(io::WKTConstants::BOUNDCRS, false); formatter->startNode(io::WKTConstants::SOURCECRS, false); d->baseCRS()->_exportToWKT(formatter); formatter->endNode(); formatter->startNode(io::WKTConstants::TARGETCRS, false); d->hubCRS()->_exportToWKT(formatter); formatter->endNode(); formatter->setAbridgedTransformation(true); d->transformation()->_exportToWKT(formatter); formatter->setAbridgedTransformation(false); formatter->endNode(); } else { auto vdatumProj4GridName = getVDatumPROJ4GRIDS(); if (!vdatumProj4GridName.empty()) { formatter->setVDatumExtension(vdatumProj4GridName); d->baseCRS()->_exportToWKT(formatter); formatter->setVDatumExtension(std::string()); return; } auto hdatumProj4GridName = getHDatumPROJ4GRIDS(); if (!hdatumProj4GridName.empty()) { formatter->setHDatumExtension(hdatumProj4GridName); d->baseCRS()->_exportToWKT(formatter); formatter->setHDatumExtension(std::string()); return; } if (!isTOWGS84Compatible()) { io::FormattingException::Throw( "Cannot export BoundCRS with non-WGS 84 hub CRS in WKT1"); } auto params = d->transformation()->getTOWGS84Parameters(); if (!formatter->useESRIDialect()) { formatter->setTOWGS84Parameters(params); } d->baseCRS()->_exportToWKT(formatter); formatter->setTOWGS84Parameters(std::vector()); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void BoundCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("BoundCRS", !identifiers().empty())); writer.AddObjKey("source_crs"); d->baseCRS()->_exportToJSON(formatter); writer.AddObjKey("target_crs"); d->hubCRS()->_exportToJSON(formatter); writer.AddObjKey("transformation"); formatter->setOmitTypeInImmediateChild(); formatter->setAbridgedTransformation(true); d->transformation()->_exportToJSON(formatter); formatter->setAbridgedTransformation(false); } //! @endcond // --------------------------------------------------------------------------- void BoundCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { auto crs_exportable = dynamic_cast(d->baseCRS_.get()); if (!crs_exportable) { io::FormattingException::Throw( "baseCRS of BoundCRS cannot be exported as a PROJ string"); } auto vdatumProj4GridName = getVDatumPROJ4GRIDS(); if (!vdatumProj4GridName.empty()) { formatter->setVDatumExtension(vdatumProj4GridName); crs_exportable->_exportToPROJString(formatter); formatter->setVDatumExtension(std::string()); } else { auto hdatumProj4GridName = getHDatumPROJ4GRIDS(); if (!hdatumProj4GridName.empty()) { formatter->setHDatumExtension(hdatumProj4GridName); crs_exportable->_exportToPROJString(formatter); formatter->setHDatumExtension(std::string()); } else { if (isTOWGS84Compatible()) { auto params = transformation()->getTOWGS84Parameters(); formatter->setTOWGS84Parameters(params); } crs_exportable->_exportToPROJString(formatter); formatter->setTOWGS84Parameters(std::vector()); } } } // --------------------------------------------------------------------------- bool BoundCRS::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherBoundCRS = dynamic_cast(other); if (otherBoundCRS == nullptr || (criterion == util::IComparable::Criterion::STRICT && !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto standardCriterion = getStandardCriterion(criterion); return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(), criterion, dbContext) && d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(), criterion, dbContext) && d->transformation_->_isEquivalentTo( otherBoundCRS->d->transformation_.get(), standardCriterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const { typedef std::pair Pair; std::list res; if (!authorityFactory) return res; std::list resMatchOfTransfToWGS84; const io::DatabaseContextNNPtr &dbContext = authorityFactory->databaseContext(); if (d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { auto resTemp = d->baseCRS_->identify(authorityFactory); std::string refTransfPROJString; bool refTransfPROJStringValid = false; auto refTransf = d->transformation_->normalizeForVisualization(); try { refTransfPROJString = refTransf->exportToPROJString( io::PROJStringFormatter::create().get()); refTransfPROJString = replaceAll( refTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 +convention=position_vector", ""); refTransfPROJStringValid = true; } catch (const std::exception &) { } bool refIsNullTransform = false; if (isTOWGS84Compatible()) { auto params = transformation()->getTOWGS84Parameters(); if (params == std::vector{0, 0, 0, 0, 0, 0, 0}) { refIsNullTransform = true; } } for (const auto &pair : resTemp) { const auto &candidateBaseCRS = pair.first; auto projCRS = dynamic_cast(candidateBaseCRS.get()); auto geodCRS = projCRS ? projCRS->baseCRS().as_nullable() : util::nn_dynamic_pointer_cast( candidateBaseCRS); if (geodCRS) { auto context = operation::CoordinateOperationContext::create( authorityFactory, nullptr, 0.0); context->setSpatialCriterion( operation::CoordinateOperationContext::SpatialCriterion:: PARTIAL_INTERSECTION); auto ops = operation::CoordinateOperationFactory::create() ->createOperations(NN_NO_CHECK(geodCRS), GeographicCRS::EPSG_4326, context); bool foundOp = false; for (const auto &op : ops) { auto opNormalized = op->normalizeForVisualization(); std::string opTransfPROJString; bool opTransfPROJStringValid = false; if (op->nameStr().find("Ballpark geographic") == 0) { if (refIsNullTransform) { res.emplace_back(create(candidateBaseCRS, d->hubCRS_, transformation()), pair.second); foundOp = true; break; } continue; } try { opTransfPROJString = opNormalized->exportToPROJString( io::PROJStringFormatter::create().get()); opTransfPROJStringValid = true; opTransfPROJString = replaceAll( opTransfPROJString, " +rx=0 +ry=0 +rz=0 +s=0 " "+convention=position_vector", ""); } catch (const std::exception &) { } if ((refTransfPROJStringValid && opTransfPROJStringValid && refTransfPROJString == opTransfPROJString) || opNormalized->_isEquivalentTo( refTransf.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { resMatchOfTransfToWGS84.emplace_back( create(candidateBaseCRS, d->hubCRS_, NN_NO_CHECK(util::nn_dynamic_pointer_cast< operation::Transformation>(op))), pair.second); foundOp = true; break; } } if (!foundOp) { res.emplace_back( create(candidateBaseCRS, d->hubCRS_, transformation()), std::min(70, pair.second)); } } } } return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DerivedGeodeticCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DerivedGeodeticCRS::~DerivedGeodeticCRS() = default; //! @endcond // --------------------------------------------------------------------------- DerivedGeodeticCRS::DerivedGeodeticCRS( const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CartesianCSNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), GeodeticCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- DerivedGeodeticCRS::DerivedGeodeticCRS( const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::SphericalCSNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), GeodeticCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- DerivedGeodeticCRS::DerivedGeodeticCRS(const DerivedGeodeticCRS &other) : SingleCRS(other), GeodeticCRS(other), DerivedCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr DerivedGeodeticCRS::_shallowClone() const { auto crs(DerivedGeodeticCRS::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Return the base CRS (a GeodeticCRS) of a DerivedGeodeticCRS. * * @return the base CRS. */ const GeodeticCRSNNPtr DerivedGeodeticCRS::baseCRS() const { return NN_NO_CHECK(util::nn_dynamic_pointer_cast( DerivedCRS::getPrivate()->baseCRS_)); } // --------------------------------------------------------------------------- /** \brief Instantiate a DerivedGeodeticCRS from a base CRS, a deriving * conversion and a cs::CartesianCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn base CRS. * @param derivingConversionIn the deriving conversion from the base CRS to this * CRS. * @param csIn the coordinate system. * @return new DerivedGeodeticCRS. */ DerivedGeodeticCRSNNPtr DerivedGeodeticCRS::create( const util::PropertyMap &properties, const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CartesianCSNNPtr &csIn) { auto crs(DerivedGeodeticCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Instantiate a DerivedGeodeticCRS from a base CRS, a deriving * conversion and a cs::SphericalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn base CRS. * @param derivingConversionIn the deriving conversion from the base CRS to this * CRS. * @param csIn the coordinate system. * @return new DerivedGeodeticCRS. */ DerivedGeodeticCRSNNPtr DerivedGeodeticCRS::create( const util::PropertyMap &properties, const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::SphericalCSNNPtr &csIn) { auto crs(DerivedGeodeticCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DerivedGeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { io::FormattingException::Throw( "DerivedGeodeticCRS can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::GEODCRS, !identifiers().empty()); formatter->addQuotedString(nameStr()); auto l_baseCRS = baseCRS(); formatter->startNode((formatter->use2019Keywords() && dynamic_cast(l_baseCRS.get())) ? io::WKTConstants::BASEGEOGCRS : io::WKTConstants::BASEGEODCRS, !baseCRS()->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); auto l_datum = l_baseCRS->datum(); if (l_datum) { l_datum->_exportToWKT(formatter); } else { auto l_datumEnsemble = datumEnsemble(); assert(l_datumEnsemble); l_datumEnsemble->_exportToWKT(formatter); } l_baseCRS->primeMeridian()->_exportToWKT(formatter); formatter->endNode(); formatter->setUseDerivingConversion(true); derivingConversionRef()->_exportToWKT(formatter); formatter->setUseDerivingConversion(false); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- void DerivedGeodeticCRS::_exportToPROJString( io::PROJStringFormatter *) const // throw(io::FormattingException) { throw io::FormattingException( "DerivedGeodeticCRS cannot be exported to PROJ string"); } // --------------------------------------------------------------------------- bool DerivedGeodeticCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> DerivedGeodeticCRS::_identify(const io::AuthorityFactoryPtr &factory) const { return CRS::_identify(factory); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DerivedGeographicCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DerivedGeographicCRS::~DerivedGeographicCRS() = default; //! @endcond // --------------------------------------------------------------------------- DerivedGeographicCRS::DerivedGeographicCRS( const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::EllipsoidalCSNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), GeographicCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- DerivedGeographicCRS::DerivedGeographicCRS(const DerivedGeographicCRS &other) : SingleCRS(other), GeographicCRS(other), DerivedCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr DerivedGeographicCRS::_shallowClone() const { auto crs(DerivedGeographicCRS::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Return the base CRS (a GeodeticCRS) of a DerivedGeographicCRS. * * @return the base CRS. */ const GeodeticCRSNNPtr DerivedGeographicCRS::baseCRS() const { return NN_NO_CHECK(util::nn_dynamic_pointer_cast( DerivedCRS::getPrivate()->baseCRS_)); } // --------------------------------------------------------------------------- /** \brief Instantiate a DerivedGeographicCRS from a base CRS, a deriving * conversion and a cs::EllipsoidalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn base CRS. * @param derivingConversionIn the deriving conversion from the base CRS to this * CRS. * @param csIn the coordinate system. * @return new DerivedGeographicCRS. */ DerivedGeographicCRSNNPtr DerivedGeographicCRS::create( const util::PropertyMap &properties, const GeodeticCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::EllipsoidalCSNNPtr &csIn) { auto crs(DerivedGeographicCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DerivedGeographicCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { io::FormattingException::Throw( "DerivedGeographicCRS can only be exported to WKT2"); } formatter->startNode(formatter->use2019Keywords() ? io::WKTConstants::GEOGCRS : io::WKTConstants::GEODCRS, !identifiers().empty()); formatter->addQuotedString(nameStr()); auto l_baseCRS = baseCRS(); formatter->startNode((formatter->use2019Keywords() && dynamic_cast(l_baseCRS.get())) ? io::WKTConstants::BASEGEOGCRS : io::WKTConstants::BASEGEODCRS, !l_baseCRS->identifiers().empty()); formatter->addQuotedString(l_baseCRS->nameStr()); l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter); l_baseCRS->primeMeridian()->_exportToWKT(formatter); formatter->endNode(); formatter->setUseDerivingConversion(true); derivingConversionRef()->_exportToWKT(formatter); formatter->setUseDerivingConversion(false); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- void DerivedGeographicCRS::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(io::FormattingException) { const auto &l_conv = derivingConversionRef(); const auto &methodName = l_conv->method()->nameStr(); if (methodName == "PROJ ob_tran o_proj=longlat" || methodName == "PROJ ob_tran o_proj=lonlat" || methodName == "PROJ ob_tran o_proj=latlong" || methodName == "PROJ ob_tran o_proj=latlon" || ci_equal(methodName, PROJ_WKT2_NAME_METHOD_POLE_ROTATION_GRIB_CONVENTION)) { l_conv->_exportToPROJString(formatter); return; } throw io::FormattingException( "DerivedGeographicCRS cannot be exported to PROJ string"); } // --------------------------------------------------------------------------- bool DerivedGeographicCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> DerivedGeographicCRS::_identify(const io::AuthorityFactoryPtr &factory) const { return CRS::_identify(factory); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DerivedProjectedCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DerivedProjectedCRS::~DerivedProjectedCRS() = default; //! @endcond // --------------------------------------------------------------------------- DerivedProjectedCRS::DerivedProjectedCRS( const ProjectedCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CoordinateSystemNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- DerivedProjectedCRS::DerivedProjectedCRS(const DerivedProjectedCRS &other) : SingleCRS(other), DerivedCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr DerivedProjectedCRS::_shallowClone() const { auto crs(DerivedProjectedCRS::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Return the base CRS (a ProjectedCRS) of a DerivedProjectedCRS. * * @return the base CRS. */ const ProjectedCRSNNPtr DerivedProjectedCRS::baseCRS() const { return NN_NO_CHECK(util::nn_dynamic_pointer_cast( DerivedCRS::getPrivate()->baseCRS_)); } // --------------------------------------------------------------------------- /** \brief Instantiate a DerivedProjectedCRS from a base CRS, a deriving * conversion and a cs::CS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn base CRS. * @param derivingConversionIn the deriving conversion from the base CRS to this * CRS. * @param csIn the coordinate system. * @return new DerivedProjectedCRS. */ DerivedProjectedCRSNNPtr DerivedProjectedCRS::create( const util::PropertyMap &properties, const ProjectedCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::CoordinateSystemNNPtr &csIn) { auto crs(DerivedProjectedCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2 || !formatter->use2019Keywords()) { io::FormattingException::Throw( "DerivedProjectedCRS can only be exported to WKT2:2019"); } formatter->startNode(io::WKTConstants::DERIVEDPROJCRS, !identifiers().empty()); formatter->addQuotedString(nameStr()); { auto l_baseProjCRS = baseCRS(); formatter->startNode(io::WKTConstants::BASEPROJCRS, !l_baseProjCRS->identifiers().empty()); formatter->addQuotedString(l_baseProjCRS->nameStr()); auto l_baseGeodCRS = l_baseProjCRS->baseCRS(); auto &geodeticCRSAxisList = l_baseGeodCRS->coordinateSystem()->axisList(); formatter->startNode( dynamic_cast(l_baseGeodCRS.get()) ? io::WKTConstants::BASEGEOGCRS : io::WKTConstants::BASEGEODCRS, !l_baseGeodCRS->identifiers().empty()); formatter->addQuotedString(l_baseGeodCRS->nameStr()); l_baseGeodCRS->exportDatumOrDatumEnsembleToWkt(formatter); // insert ellipsoidal cs unit when the units of the map // projection angular parameters are not explicitly given within those // parameters. See // http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#61 if (formatter->primeMeridianOrParameterUnitOmittedIfSameAsAxis() && !geodeticCRSAxisList.empty()) { geodeticCRSAxisList[0]->unit()._exportToWKT(formatter); } l_baseGeodCRS->primeMeridian()->_exportToWKT(formatter); formatter->endNode(); l_baseProjCRS->derivingConversionRef()->_exportToWKT(formatter); formatter->endNode(); } formatter->setUseDerivingConversion(true); derivingConversionRef()->_exportToWKT(formatter); formatter->setUseDerivingConversion(false); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- bool DerivedProjectedCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct TemporalCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalCRS::~TemporalCRS() = default; //! @endcond // --------------------------------------------------------------------------- TemporalCRS::TemporalCRS(const datum::TemporalDatumNNPtr &datumIn, const cs::TemporalCSNNPtr &csIn) : SingleCRS(datumIn.as_nullable(), nullptr, csIn), d(nullptr) {} // --------------------------------------------------------------------------- TemporalCRS::TemporalCRS(const TemporalCRS &other) : SingleCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr TemporalCRS::_shallowClone() const { auto crs(TemporalCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the datum::TemporalDatum associated with the CRS. * * @return a TemporalDatum */ const datum::TemporalDatumNNPtr TemporalCRS::datum() const { return NN_NO_CHECK(std::static_pointer_cast( SingleCRS::getPrivate()->datum)); } // --------------------------------------------------------------------------- /** \brief Return the cs::TemporalCS associated with the CRS. * * @return a TemporalCS */ const cs::TemporalCSNNPtr TemporalCRS::coordinateSystem() const { return util::nn_static_pointer_cast( SingleCRS::getPrivate()->coordinateSystem); } // --------------------------------------------------------------------------- /** \brief Instantiate a TemporalCRS from a datum and a coordinate system. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datumIn the datum. * @param csIn the coordinate system. * @return new TemporalCRS. */ TemporalCRSNNPtr TemporalCRS::create(const util::PropertyMap &properties, const datum::TemporalDatumNNPtr &datumIn, const cs::TemporalCSNNPtr &csIn) { auto crs(TemporalCRS::nn_make_shared(datumIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void TemporalCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { io::FormattingException::Throw( "TemporalCRS can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::TIMECRS, !identifiers().empty()); formatter->addQuotedString(nameStr()); datum()->_exportToWKT(formatter); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void TemporalCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("TemporalCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("datum"); formatter->setOmitTypeInImmediateChild(); datum()->_exportToJSON(formatter); writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- bool TemporalCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherTemporalCRS = dynamic_cast(other); return otherTemporalCRS != nullptr && SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct EngineeringCRS::Private { bool forceOutputCS_ = false; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress EngineeringCRS::~EngineeringCRS() = default; //! @endcond // --------------------------------------------------------------------------- EngineeringCRS::EngineeringCRS(const datum::EngineeringDatumNNPtr &datumIn, const cs::CoordinateSystemNNPtr &csIn) : SingleCRS(datumIn.as_nullable(), nullptr, csIn), d(internal::make_unique()) {} // --------------------------------------------------------------------------- EngineeringCRS::EngineeringCRS(const EngineeringCRS &other) : SingleCRS(other), d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- CRSNNPtr EngineeringCRS::_shallowClone() const { auto crs(EngineeringCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the datum::EngineeringDatum associated with the CRS. * * @return a EngineeringDatum */ const datum::EngineeringDatumNNPtr EngineeringCRS::datum() const { return NN_NO_CHECK(std::static_pointer_cast( SingleCRS::getPrivate()->datum)); } // --------------------------------------------------------------------------- /** \brief Instantiate a EngineeringCRS from a datum and a coordinate system. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datumIn the datum. * @param csIn the coordinate system. * @return new EngineeringCRS. */ EngineeringCRSNNPtr EngineeringCRS::create(const util::PropertyMap &properties, const datum::EngineeringDatumNNPtr &datumIn, const cs::CoordinateSystemNNPtr &csIn) { auto crs(EngineeringCRS::nn_make_shared(datumIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); const auto pVal = properties.get("FORCE_OUTPUT_CS"); if (pVal) { if (const auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == util::BoxedValue::Type::BOOLEAN && genVal->booleanValue()) { crs->d->forceOutputCS_ = true; } } } return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void EngineeringCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::ENGCRS : io::WKTConstants::LOCAL_CS, !identifiers().empty()); formatter->addQuotedString(nameStr()); if (isWKT2 || !datum()->nameStr().empty()) { datum()->_exportToWKT(formatter); coordinateSystem()->_exportToWKT(formatter); } if (!isWKT2 && d->forceOutputCS_) { coordinateSystem()->axisList()[0]->unit()._exportToWKT(formatter); } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void EngineeringCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("EngineeringCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("datum"); formatter->setOmitTypeInImmediateChild(); datum()->_exportToJSON(formatter); writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- bool EngineeringCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherEngineeringCRS = dynamic_cast(other); return otherEngineeringCRS != nullptr && SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ParametricCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ParametricCRS::~ParametricCRS() = default; //! @endcond // --------------------------------------------------------------------------- ParametricCRS::ParametricCRS(const datum::ParametricDatumNNPtr &datumIn, const cs::ParametricCSNNPtr &csIn) : SingleCRS(datumIn.as_nullable(), nullptr, csIn), d(nullptr) {} // --------------------------------------------------------------------------- ParametricCRS::ParametricCRS(const ParametricCRS &other) : SingleCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr ParametricCRS::_shallowClone() const { auto crs(ParametricCRS::nn_make_shared(*this)); crs->assignSelf(crs); return crs; } // --------------------------------------------------------------------------- /** \brief Return the datum::ParametricDatum associated with the CRS. * * @return a ParametricDatum */ const datum::ParametricDatumNNPtr ParametricCRS::datum() const { return NN_NO_CHECK(std::static_pointer_cast( SingleCRS::getPrivate()->datum)); } // --------------------------------------------------------------------------- /** \brief Return the cs::TemporalCS associated with the CRS. * * @return a TemporalCS */ const cs::ParametricCSNNPtr ParametricCRS::coordinateSystem() const { return util::nn_static_pointer_cast( SingleCRS::getPrivate()->coordinateSystem); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParametricCRS from a datum and a coordinate system. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datumIn the datum. * @param csIn the coordinate system. * @return new ParametricCRS. */ ParametricCRSNNPtr ParametricCRS::create(const util::PropertyMap &properties, const datum::ParametricDatumNNPtr &datumIn, const cs::ParametricCSNNPtr &csIn) { auto crs(ParametricCRS::nn_make_shared(datumIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ParametricCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { io::FormattingException::Throw( "ParametricCRS can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::PARAMETRICCRS, !identifiers().empty()); formatter->addQuotedString(nameStr()); datum()->_exportToWKT(formatter); coordinateSystem()->_exportToWKT(formatter); ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ParametricCRS::_exportToJSON( io::JSONFormatter *formatter) const // throw(io::FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("ParametricCRS", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("datum"); formatter->setOmitTypeInImmediateChild(); datum()->_exportToJSON(formatter); writer.AddObjKey("coordinate_system"); formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- bool ParametricCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherParametricCRS = dynamic_cast(other); return otherParametricCRS != nullptr && SingleCRS::baseIsEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DerivedVerticalCRS::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DerivedVerticalCRS::~DerivedVerticalCRS() = default; //! @endcond // --------------------------------------------------------------------------- DerivedVerticalCRS::DerivedVerticalCRS( const VerticalCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::VerticalCSNNPtr &csIn) : SingleCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), VerticalCRS(baseCRSIn->datum(), baseCRSIn->datumEnsemble(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- DerivedVerticalCRS::DerivedVerticalCRS(const DerivedVerticalCRS &other) : SingleCRS(other), VerticalCRS(other), DerivedCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- CRSNNPtr DerivedVerticalCRS::_shallowClone() const { auto crs(DerivedVerticalCRS::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- /** \brief Return the base CRS (a VerticalCRS) of a DerivedVerticalCRS. * * @return the base CRS. */ const VerticalCRSNNPtr DerivedVerticalCRS::baseCRS() const { return NN_NO_CHECK(util::nn_dynamic_pointer_cast( DerivedCRS::getPrivate()->baseCRS_)); } // --------------------------------------------------------------------------- /** \brief Instantiate a DerivedVerticalCRS from a base CRS, a deriving * conversion and a cs::VerticalCS. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param baseCRSIn base CRS. * @param derivingConversionIn the deriving conversion from the base CRS to this * CRS. * @param csIn the coordinate system. * @return new DerivedVerticalCRS. */ DerivedVerticalCRSNNPtr DerivedVerticalCRS::create( const util::PropertyMap &properties, const VerticalCRSNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const cs::VerticalCSNNPtr &csIn) { auto crs(DerivedVerticalCRS::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DerivedVerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { io::FormattingException::Throw( "DerivedVerticalCRS can only be exported to WKT2"); } baseExportToWKT(formatter, io::WKTConstants::VERTCRS, io::WKTConstants::BASEVERTCRS); } //! @endcond // --------------------------------------------------------------------------- void DerivedVerticalCRS::_exportToPROJString( io::PROJStringFormatter *) const // throw(io::FormattingException) { throw io::FormattingException( "DerivedVerticalCRS cannot be exported to PROJ string"); } // --------------------------------------------------------------------------- bool DerivedVerticalCRS::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list> DerivedVerticalCRS::_identify(const io::AuthorityFactoryPtr &factory) const { return CRS::_identify(factory); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress template struct DerivedCRSTemplate::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress template DerivedCRSTemplate::~DerivedCRSTemplate() = default; //! @endcond // --------------------------------------------------------------------------- template DerivedCRSTemplate::DerivedCRSTemplate( const BaseNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const CSNNPtr &csIn) : SingleCRS(baseCRSIn->datum().as_nullable(), nullptr, csIn), BaseType(baseCRSIn->datum(), csIn), DerivedCRS(baseCRSIn, derivingConversionIn, csIn), d(nullptr) {} // --------------------------------------------------------------------------- template DerivedCRSTemplate::DerivedCRSTemplate( const DerivedCRSTemplate &other) : SingleCRS(other), BaseType(other), DerivedCRS(other), d(nullptr) {} // --------------------------------------------------------------------------- template const typename DerivedCRSTemplate::BaseNNPtr DerivedCRSTemplate::baseCRS() const { auto l_baseCRS = DerivedCRS::getPrivate()->baseCRS_; return NN_NO_CHECK(util::nn_dynamic_pointer_cast(l_baseCRS)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress template CRSNNPtr DerivedCRSTemplate::_shallowClone() const { auto crs(DerivedCRSTemplate::nn_make_shared(*this)); crs->assignSelf(crs); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- template typename DerivedCRSTemplate::NNPtr DerivedCRSTemplate::create( const util::PropertyMap &properties, const BaseNNPtr &baseCRSIn, const operation::ConversionNNPtr &derivingConversionIn, const CSNNPtr &csIn) { auto crs(DerivedCRSTemplate::nn_make_shared( baseCRSIn, derivingConversionIn, csIn)); crs->assignSelf(crs); crs->setProperties(properties); crs->setDerivingConversionCRS(); return crs; } // --------------------------------------------------------------------------- template const char *DerivedCRSTemplate::className() const { return DerivedCRSTraits::CRSName().c_str(); } // --------------------------------------------------------------------------- static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *formatter, const std::string &crsName, bool wkt2_2019_only) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2 || (wkt2_2019_only && !formatter->use2019Keywords())) { io::FormattingException::Throw(crsName + " can only be exported to WKT2" + (wkt2_2019_only ? ":2019" : "")); } } // --------------------------------------------------------------------------- template void DerivedCRSTemplate::_exportToWKT( io::WKTFormatter *formatter) const { DerivedCRSTemplateCheckExportToWKT(formatter, DerivedCRSTraits::CRSName(), DerivedCRSTraits::wkt2_2019_only); baseExportToWKT(formatter, DerivedCRSTraits::WKTKeyword(), DerivedCRSTraits::WKTBaseKeyword()); } // --------------------------------------------------------------------------- template bool DerivedCRSTemplate::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDerivedCRS = dynamic_cast(other); return otherDerivedCRS != nullptr && DerivedCRS::_isEquivalentTo(other, criterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string STRING_DerivedEngineeringCRS("DerivedEngineeringCRS"); const std::string &DerivedEngineeringCRSTraits::CRSName() { return STRING_DerivedEngineeringCRS; } const std::string &DerivedEngineeringCRSTraits::WKTKeyword() { return io::WKTConstants::ENGCRS; } const std::string &DerivedEngineeringCRSTraits::WKTBaseKeyword() { return io::WKTConstants::BASEENGCRS; } template class DerivedCRSTemplate; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string STRING_DerivedParametricCRS("DerivedParametricCRS"); const std::string &DerivedParametricCRSTraits::CRSName() { return STRING_DerivedParametricCRS; } const std::string &DerivedParametricCRSTraits::WKTKeyword() { return io::WKTConstants::PARAMETRICCRS; } const std::string &DerivedParametricCRSTraits::WKTBaseKeyword() { return io::WKTConstants::BASEPARAMCRS; } template class DerivedCRSTemplate; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string STRING_DerivedTemporalCRS("DerivedTemporalCRS"); const std::string &DerivedTemporalCRSTraits::CRSName() { return STRING_DerivedTemporalCRS; } const std::string &DerivedTemporalCRSTraits::WKTKeyword() { return io::WKTConstants::TIMECRS; } const std::string &DerivedTemporalCRSTraits::WKTBaseKeyword() { return io::WKTConstants::BASETIMECRS; } template class DerivedCRSTemplate; //! @endcond // --------------------------------------------------------------------------- } // namespace crs NS_PROJ_END proj-6.3.1/src/iso19111/metadata.cpp0000664000175000017500000012476213601752712013700 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/metadata.hpp" #include "proj/common.hpp" #include "proj/io.hpp" #include "proj/util.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include #include #include #include using namespace NS_PROJ::internal; using namespace NS_PROJ::io; using namespace NS_PROJ::util; #if 0 namespace dropbox{ namespace oxygen { template<> nn>::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace metadata { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Citation::Private { optional title{}; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Citation::Citation() : d(internal::make_unique()) {} //! @endcond // --------------------------------------------------------------------------- /** \brief Constructs a citation by its title. */ Citation::Citation(const std::string &titleIn) : d(internal::make_unique()) { d->title = titleIn; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Citation::Citation(const Citation &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- Citation::~Citation() = default; // --------------------------------------------------------------------------- Citation &Citation::operator=(const Citation &other) { if (this != &other) { *d = *other.d; } return *this; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns the name by which the cited resource is known. */ const optional &Citation::title() PROJ_PURE_DEFN { return d->title; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeographicExtent::Private {}; //! @endcond // --------------------------------------------------------------------------- GeographicExtent::GeographicExtent() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeographicExtent::~GeographicExtent() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeographicBoundingBox::Private { double west_{}; double south_{}; double east_{}; double north_{}; Private(double west, double south, double east, double north) : west_(west), south_(south), east_(east), north_(north) {} bool intersects(const Private &other) const; std::unique_ptr intersection(const Private &other) const; }; //! @endcond // --------------------------------------------------------------------------- GeographicBoundingBox::GeographicBoundingBox(double west, double south, double east, double north) : GeographicExtent(), d(internal::make_unique(west, south, east, north)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeographicBoundingBox::~GeographicBoundingBox() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Returns the western-most coordinate of the limit of the dataset * extent. * * The unit is degrees. * * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses * the anti-meridian. */ double GeographicBoundingBox::westBoundLongitude() PROJ_PURE_DEFN { return d->west_; } // --------------------------------------------------------------------------- /** \brief Returns the southern-most coordinate of the limit of the dataset * extent. * * The unit is degrees. */ double GeographicBoundingBox::southBoundLatitude() PROJ_PURE_DEFN { return d->south_; } // --------------------------------------------------------------------------- /** \brief Returns the eastern-most coordinate of the limit of the dataset * extent. * * The unit is degrees. * * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses * the anti-meridian. */ double GeographicBoundingBox::eastBoundLongitude() PROJ_PURE_DEFN { return d->east_; } // --------------------------------------------------------------------------- /** \brief Returns the northern-most coordinate of the limit of the dataset * extent. * * The unit is degrees. */ double GeographicBoundingBox::northBoundLatitude() PROJ_PURE_DEFN { return d->north_; } // --------------------------------------------------------------------------- /** \brief Instantiate a GeographicBoundingBox. * * If east < west, then the bounding box crosses the anti-meridian. * * @param west Western-most coordinate of the limit of the dataset extent (in * degrees). * @param south Southern-most coordinate of the limit of the dataset extent (in * degrees). * @param east Eastern-most coordinate of the limit of the dataset extent (in * degrees). * @param north Northern-most coordinate of the limit of the dataset extent (in * degrees). * @return a new GeographicBoundingBox. */ GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west, double south, double east, double north) { return GeographicBoundingBox::nn_make_shared( west, south, east, north); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool GeographicBoundingBox::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion, const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; return d->west_ == otherExtent->d->west_ && d->south_ == otherExtent->d->south_ && d->east_ == otherExtent->d->east_ && d->north_ == otherExtent->d->north_; } //! @endcond // --------------------------------------------------------------------------- bool GeographicBoundingBox::contains(const GeographicExtentNNPtr &other) const { auto otherExtent = dynamic_cast(other.get()); if (!otherExtent) { return false; } const double W = d->west_; const double E = d->east_; const double N = d->north_; const double S = d->south_; const double oW = otherExtent->d->west_; const double oE = otherExtent->d->east_; const double oN = otherExtent->d->north_; const double oS = otherExtent->d->south_; if (!(S <= oS && N >= oN)) { return false; } if (W == -180.0 && E == 180.0) { return true; } if (oW == -180.0 && oE == 180.0) { return false; } // Normal bounding box ? if (W < E) { if (oW < oE) { return W <= oW && E >= oE; } else { return false; } // No: crossing antimerian } else { if (oW < oE) { if (oW >= W) { return true; } else if (oE <= E) { return true; } else { return false; } } else { return W <= oW && E >= oE; } } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool GeographicBoundingBox::Private::intersects(const Private &other) const { const double W = west_; const double E = east_; const double N = north_; const double S = south_; const double oW = other.west_; const double oE = other.east_; const double oN = other.north_; const double oS = other.south_; if (N < oS || S > oN) { return false; } if (W == -180.0 && E == 180.0 && oW > oE) { return true; } if (oW == -180.0 && oE == 180.0 && W > E) { return true; } // Normal bounding box ? if (W <= E) { if (oW < oE) { if (std::max(W, oW) < std::min(E, oE)) { return true; } return false; } return intersects(Private(oW, oS, 180.0, oN)) || intersects(Private(-180.0, oS, oE, oN)); // No: crossing antimerian } else { if (oW <= oE) { return other.intersects(*this); } return true; } } //! @endcond bool GeographicBoundingBox::intersects( const GeographicExtentNNPtr &other) const { auto otherExtent = dynamic_cast(other.get()); if (!otherExtent) { return false; } return d->intersects(*(otherExtent->d)); } // --------------------------------------------------------------------------- GeographicExtentPtr GeographicBoundingBox::intersection(const GeographicExtentNNPtr &other) const { auto otherExtent = dynamic_cast(other.get()); if (!otherExtent) { return nullptr; } auto ret = d->intersection(*(otherExtent->d)); if (ret) { auto bbox = GeographicBoundingBox::create(ret->west_, ret->south_, ret->east_, ret->north_); return bbox.as_nullable(); } return nullptr; } //! @cond Doxygen_Suppress std::unique_ptr GeographicBoundingBox::Private::intersection(const Private &otherExtent) const { const double W = west_; const double E = east_; const double N = north_; const double S = south_; const double oW = otherExtent.west_; const double oE = otherExtent.east_; const double oN = otherExtent.north_; const double oS = otherExtent.south_; if (N < oS || S > oN) { return nullptr; } if (W == -180.0 && E == 180.0 && oW > oE) { return internal::make_unique(oW, std::max(S, oS), oE, std::min(N, oN)); } if (oW == -180.0 && oE == 180.0 && W > E) { return internal::make_unique(W, std::max(S, oS), E, std::min(N, oN)); } // Normal bounding box ? if (W <= E) { if (oW < oE) { auto res = internal::make_unique( std::max(W, oW), std::max(S, oS), std::min(E, oE), std::min(N, oN)); if (res->west_ < res->east_) { return res; } return nullptr; } // Return larger of two parts of the multipolygon auto inter1 = intersection(Private(oW, oS, 180.0, oN)); auto inter2 = intersection(Private(-180.0, oS, oE, oN)); if (!inter1) { return inter2; } if (!inter2) { return inter1; } if (inter1->east_ - inter1->west_ > inter2->east_ - inter2->west_) { return inter1; } return inter2; // No: crossing antimerian } else { if (oW <= oE) { return otherExtent.intersection(*this); } return internal::make_unique(std::max(W, oW), std::max(S, oS), std::min(E, oE), std::min(N, oN)); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct VerticalExtent::Private { double minimum_{}; double maximum_{}; common::UnitOfMeasureNNPtr unit_; Private(double minimum, double maximum, const common::UnitOfMeasureNNPtr &unit) : minimum_(minimum), maximum_(maximum), unit_(unit) {} }; //! @endcond // --------------------------------------------------------------------------- VerticalExtent::VerticalExtent(double minimumIn, double maximumIn, const common::UnitOfMeasureNNPtr &unitIn) : d(internal::make_unique(minimumIn, maximumIn, unitIn)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalExtent::~VerticalExtent() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Returns the minimum of the vertical extent. */ double VerticalExtent::minimumValue() PROJ_PURE_DEFN { return d->minimum_; } // --------------------------------------------------------------------------- /** \brief Returns the maximum of the vertical extent. */ double VerticalExtent::maximumValue() PROJ_PURE_DEFN { return d->maximum_; } // --------------------------------------------------------------------------- /** \brief Returns the unit of the vertical extent. */ common::UnitOfMeasureNNPtr &VerticalExtent::unit() PROJ_PURE_DEFN { return d->unit_; } // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalExtent. * * @param minimumIn minimum. * @param maximumIn maximum. * @param unitIn unit. * @return a new VerticalExtent. */ VerticalExtentNNPtr VerticalExtent::create(double minimumIn, double maximumIn, const common::UnitOfMeasureNNPtr &unitIn) { return VerticalExtent::nn_make_shared(minimumIn, maximumIn, unitIn); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool VerticalExtent::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion, const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; return d->minimum_ == otherExtent->d->minimum_ && d->maximum_ == otherExtent->d->maximum_ && d->unit_ == otherExtent->d->unit_; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether this extent contains the other one. */ bool VerticalExtent::contains(const VerticalExtentNNPtr &other) const { const double thisUnitToSI = d->unit_->conversionToSI(); const double otherUnitToSI = other->d->unit_->conversionToSI(); return d->minimum_ * thisUnitToSI <= other->d->minimum_ * otherUnitToSI && d->maximum_ * thisUnitToSI >= other->d->maximum_ * otherUnitToSI; } // --------------------------------------------------------------------------- /** \brief Returns whether this extent intersects the other one. */ bool VerticalExtent::intersects(const VerticalExtentNNPtr &other) const { const double thisUnitToSI = d->unit_->conversionToSI(); const double otherUnitToSI = other->d->unit_->conversionToSI(); return d->minimum_ * thisUnitToSI <= other->d->maximum_ * otherUnitToSI && d->maximum_ * thisUnitToSI >= other->d->minimum_ * otherUnitToSI; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct TemporalExtent::Private { std::string start_{}; std::string stop_{}; Private(const std::string &start, const std::string &stop) : start_(start), stop_(stop) {} }; //! @endcond // --------------------------------------------------------------------------- TemporalExtent::TemporalExtent(const std::string &startIn, const std::string &stopIn) : d(internal::make_unique(startIn, stopIn)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalExtent::~TemporalExtent() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Returns the start of the temporal extent. */ const std::string &TemporalExtent::start() PROJ_PURE_DEFN { return d->start_; } // --------------------------------------------------------------------------- /** \brief Returns the end of the temporal extent. */ const std::string &TemporalExtent::stop() PROJ_PURE_DEFN { return d->stop_; } // --------------------------------------------------------------------------- /** \brief Instantiate a TemporalExtent. * * @param start start. * @param stop stop. * @return a new TemporalExtent. */ TemporalExtentNNPtr TemporalExtent::create(const std::string &start, const std::string &stop) { return TemporalExtent::nn_make_shared(start, stop); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool TemporalExtent::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion, const io::DatabaseContextPtr &) const { auto otherExtent = dynamic_cast(other); if (!otherExtent) return false; return start() == otherExtent->start() && stop() == otherExtent->stop(); } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether this extent contains the other one. */ bool TemporalExtent::contains(const TemporalExtentNNPtr &other) const { return start() <= other->start() && stop() >= other->stop(); } // --------------------------------------------------------------------------- /** \brief Returns whether this extent intersects the other one. */ bool TemporalExtent::intersects(const TemporalExtentNNPtr &other) const { return start() <= other->stop() && stop() >= other->start(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Extent::Private { optional description_{}; std::vector geographicElements_{}; std::vector verticalElements_{}; std::vector temporalElements_{}; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Extent::Extent() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- Extent::Extent(const Extent &other) : d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- Extent::~Extent() = default; //! @endcond // --------------------------------------------------------------------------- /** Return a textual description of the extent. * * @return the description, or empty. */ const optional &Extent::description() PROJ_PURE_DEFN { return d->description_; } // --------------------------------------------------------------------------- /** Return the geographic element(s) of the extent * * @return the geographic element(s), or empty. */ const std::vector & Extent::geographicElements() PROJ_PURE_DEFN { return d->geographicElements_; } // --------------------------------------------------------------------------- /** Return the vertical element(s) of the extent * * @return the vertical element(s), or empty. */ const std::vector & Extent::verticalElements() PROJ_PURE_DEFN { return d->verticalElements_; } // --------------------------------------------------------------------------- /** Return the temporal element(s) of the extent * * @return the temporal element(s), or empty. */ const std::vector & Extent::temporalElements() PROJ_PURE_DEFN { return d->temporalElements_; } // --------------------------------------------------------------------------- /** \brief Instantiate a Extent. * * @param descriptionIn Textual description, or empty. * @param geographicElementsIn Geographic element(s), or empty. * @param verticalElementsIn Vertical element(s), or empty. * @param temporalElementsIn Temporal element(s), or empty. * @return a new Extent. */ ExtentNNPtr Extent::create(const optional &descriptionIn, const std::vector &geographicElementsIn, const std::vector &verticalElementsIn, const std::vector &temporalElementsIn) { auto extent = Extent::nn_make_shared(); extent->assignSelf(extent); extent->d->description_ = descriptionIn; extent->d->geographicElements_ = geographicElementsIn; extent->d->verticalElements_ = verticalElementsIn; extent->d->temporalElements_ = temporalElementsIn; return extent; } // --------------------------------------------------------------------------- /** \brief Instantiate a Extent from a bounding box * * @param west Western-most coordinate of the limit of the dataset extent (in * degrees). * @param south Southern-most coordinate of the limit of the dataset extent (in * degrees). * @param east Eastern-most coordinate of the limit of the dataset extent (in * degrees). * @param north Northern-most coordinate of the limit of the dataset extent (in * degrees). * @param descriptionIn Textual description, or empty. * @return a new Extent. */ ExtentNNPtr Extent::createFromBBOX(double west, double south, double east, double north, const util::optional &descriptionIn) { return create( descriptionIn, std::vector{ nn_static_pointer_cast( GeographicBoundingBox::create(west, south, east, north))}, std::vector(), std::vector()); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool Extent::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherExtent = dynamic_cast(other); bool ret = (otherExtent && description().has_value() == otherExtent->description().has_value() && *description() == *otherExtent->description() && d->geographicElements_.size() == otherExtent->d->geographicElements_.size() && d->verticalElements_.size() == otherExtent->d->verticalElements_.size() && d->temporalElements_.size() == otherExtent->d->temporalElements_.size()); if (ret) { for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) { ret = d->geographicElements_[i]->_isEquivalentTo( otherExtent->d->geographicElements_[i].get(), criterion, dbContext); } for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) { ret = d->verticalElements_[i]->_isEquivalentTo( otherExtent->d->verticalElements_[i].get(), criterion, dbContext); } for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) { ret = d->temporalElements_[i]->_isEquivalentTo( otherExtent->d->temporalElements_[i].get(), criterion, dbContext); } } return ret; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether this extent contains the other one. * * Behaviour only well specified if each sub-extent category as at most * one element. */ bool Extent::contains(const ExtentNNPtr &other) const { bool res = true; if (d->geographicElements_.size() == 1 && other->d->geographicElements_.size() == 1) { res = d->geographicElements_[0]->contains( other->d->geographicElements_[0]); } if (res && d->verticalElements_.size() == 1 && other->d->verticalElements_.size() == 1) { res = d->verticalElements_[0]->contains(other->d->verticalElements_[0]); } if (res && d->temporalElements_.size() == 1 && other->d->temporalElements_.size() == 1) { res = d->temporalElements_[0]->contains(other->d->temporalElements_[0]); } return res; } // --------------------------------------------------------------------------- /** \brief Returns whether this extent intersects the other one. * * Behaviour only well specified if each sub-extent category as at most * one element. */ bool Extent::intersects(const ExtentNNPtr &other) const { bool res = true; if (d->geographicElements_.size() == 1 && other->d->geographicElements_.size() == 1) { res = d->geographicElements_[0]->intersects( other->d->geographicElements_[0]); } if (res && d->verticalElements_.size() == 1 && other->d->verticalElements_.size() == 1) { res = d->verticalElements_[0]->intersects(other->d->verticalElements_[0]); } if (res && d->temporalElements_.size() == 1 && other->d->temporalElements_.size() == 1) { res = d->temporalElements_[0]->intersects(other->d->temporalElements_[0]); } return res; } // --------------------------------------------------------------------------- /** \brief Returns the intersection of this extent with another one. * * Behaviour only well specified if there is one single GeographicExtent * in each object. * Returns nullptr otherwise. */ ExtentPtr Extent::intersection(const ExtentNNPtr &other) const { if (d->geographicElements_.size() == 1 && other->d->geographicElements_.size() == 1) { if (contains(other)) { return other.as_nullable(); } auto self = util::nn_static_pointer_cast(shared_from_this()); if (other->contains(self)) { return self.as_nullable(); } auto geogIntersection = d->geographicElements_[0]->intersection( other->d->geographicElements_[0]); if (geogIntersection) { return create(util::optional(), std::vector{ NN_NO_CHECK(geogIntersection)}, std::vector{}, std::vector{}); } } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Identifier::Private { optional authority_{}; std::string code_{}; optional codeSpace_{}; optional version_{}; optional description_{}; optional uri_{}; Private() = default; Private(const std::string &codeIn, const PropertyMap &properties) : code_(codeIn) { setProperties(properties); } private: // cppcheck-suppress functionStatic void setProperties(const PropertyMap &properties); }; // --------------------------------------------------------------------------- void Identifier::Private::setProperties( const PropertyMap &properties) // throw(InvalidValueTypeException) { { const auto pVal = properties.get(AUTHORITY_KEY); if (pVal) { if (auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == BoxedValue::Type::STRING) { authority_ = Citation(genVal->stringValue()); } else { throw InvalidValueTypeException("Invalid value type for " + AUTHORITY_KEY); } } else { if (auto citation = dynamic_cast(pVal->get())) { authority_ = *citation; } else { throw InvalidValueTypeException("Invalid value type for " + AUTHORITY_KEY); } } } } { const auto pVal = properties.get(CODE_KEY); if (pVal) { if (auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == BoxedValue::Type::INTEGER) { code_ = toString(genVal->integerValue()); } else if (genVal->type() == BoxedValue::Type::STRING) { code_ = genVal->stringValue(); } else { throw InvalidValueTypeException("Invalid value type for " + CODE_KEY); } } else { throw InvalidValueTypeException("Invalid value type for " + CODE_KEY); } } } properties.getStringValue(CODESPACE_KEY, codeSpace_); properties.getStringValue(VERSION_KEY, version_); properties.getStringValue(DESCRIPTION_KEY, description_); properties.getStringValue(URI_KEY, uri_); } //! @endcond // --------------------------------------------------------------------------- Identifier::Identifier(const std::string &codeIn, const util::PropertyMap &properties) : d(internal::make_unique(codeIn, properties)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- Identifier::Identifier() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- Identifier::Identifier(const Identifier &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- Identifier::~Identifier() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Identifier. * * @param codeIn Alphanumeric value identifying an instance in the codespace * @param properties See \ref general_properties. * Generally, the Identifier::CODESPACE_KEY should be set. * @return a new Identifier. */ IdentifierNNPtr Identifier::create(const std::string &codeIn, const PropertyMap &properties) { return Identifier::nn_make_shared(codeIn, properties); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress IdentifierNNPtr Identifier::createFromDescription(const std::string &descriptionIn) { auto id = Identifier::nn_make_shared(); id->d->description_ = descriptionIn; return id; } //! @endcond // --------------------------------------------------------------------------- /** \brief Return a citation for the organization responsible for definition and * maintenance of the code. * * @return the citation for the authority, or empty. */ const optional &Identifier::authority() PROJ_PURE_DEFN { return d->authority_; } // --------------------------------------------------------------------------- /** \brief Return the alphanumeric value identifying an instance in the * codespace. * * e.g. "4326" (for EPSG:4326 WGS 84 GeographicCRS) * * @return the code. */ const std::string &Identifier::code() PROJ_PURE_DEFN { return d->code_; } // --------------------------------------------------------------------------- /** \brief Return the organization responsible for definition and maintenance of * the code. * * e.g "EPSG" * * @return the authority codespace, or empty. */ const optional &Identifier::codeSpace() PROJ_PURE_DEFN { return d->codeSpace_; } // --------------------------------------------------------------------------- /** \brief Return the version identifier for the namespace. * * When appropriate, the edition is identified by the effective date, coded * using ISO 8601 date format. * * @return the version or empty. */ const optional &Identifier::version() PROJ_PURE_DEFN { return d->version_; } // --------------------------------------------------------------------------- /** \brief Return the natural language description of the meaning of the code * value. * * @return the description or empty. */ const optional &Identifier::description() PROJ_PURE_DEFN { return d->description_; } // --------------------------------------------------------------------------- /** \brief Return the URI of the identifier. * * @return the URI or empty. */ const optional &Identifier::uri() PROJ_PURE_DEFN { return d->uri_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Identifier::_exportToWKT(WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; const std::string &l_code = code(); const std::string &l_codeSpace = *codeSpace(); if (!l_codeSpace.empty() && !l_code.empty()) { if (isWKT2) { formatter->startNode(WKTConstants::ID, false); formatter->addQuotedString(l_codeSpace); try { (void)std::stoi(l_code); formatter->add(l_code); } catch (const std::exception &) { formatter->addQuotedString(l_code); } if (version().has_value()) { auto l_version = *(version()); try { (void)c_locale_stod(l_version); formatter->add(l_version); } catch (const std::exception &) { formatter->addQuotedString(l_version); } } if (authority().has_value() && *(authority()->title()) != l_codeSpace) { formatter->startNode(WKTConstants::CITATION, false); formatter->addQuotedString(*(authority()->title())); formatter->endNode(); } if (uri().has_value()) { formatter->startNode(WKTConstants::URI, false); formatter->addQuotedString(*(uri())); formatter->endNode(); } formatter->endNode(); } else { formatter->startNode(WKTConstants::AUTHORITY, false); formatter->addQuotedString(l_codeSpace); formatter->addQuotedString(l_code); formatter->endNode(); } } } // --------------------------------------------------------------------------- void Identifier::_exportToJSON(JSONFormatter *formatter) const { const std::string &l_code = code(); const std::string &l_codeSpace = *codeSpace(); if (!l_codeSpace.empty() && !l_code.empty()) { auto &writer = formatter->writer(); auto objContext(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("authority"); writer.Add(l_codeSpace); writer.AddObjKey("code"); try { writer.Add(std::stoi(l_code)); } catch (const std::exception &) { writer.Add(l_code); } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool isIgnoredChar(char ch) { return ch == ' ' || ch == '_' || ch == '-' || ch == '/' || ch == '(' || ch == ')' || ch == '.' || ch == '&'; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const struct utf8_to_lower { const char *utf8; char ascii; } map_utf8_to_lower[] = { {"\xc3\xa1", 'a'}, // a acute {"\xc3\xa4", 'a'}, // a tremma {"\xc4\x9b", 'e'}, // e reverse circumflex {"\xc3\xa8", 'e'}, // e grave {"\xc3\xa9", 'e'}, // e acute {"\xc3\xab", 'e'}, // e tremma {"\xc3\xad", 'i'}, // i grave {"\xc3\xb4", 'o'}, // o circumflex {"\xc3\xb6", 'o'}, // o tremma {"\xc3\xa7", 'c'}, // c cedilla }; static const struct utf8_to_lower *get_ascii_replacement(const char *c_str) { for (const auto &pair : map_utf8_to_lower) { if (*c_str == pair.utf8[0] && strncmp(c_str, pair.utf8, strlen(pair.utf8)) == 0) { return &pair; } } return nullptr; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::string Identifier::canonicalizeName(const std::string &str) { std::string res; const char *c_str = str.c_str(); for (size_t i = 0; c_str[i] != 0; ++i) { const auto ch = c_str[i]; if (ch == ' ' && c_str[i + 1] == '+' && c_str[i + 2] == ' ') { i += 2; continue; } if (ch == '1' && !res.empty() && !(res.back() >= '0' && res.back() <= '9') && c_str[i + 1] == '9' && c_str[i + 2] >= '0' && c_str[i + 2] <= '9') { ++i; continue; } if (static_cast(ch) > 127) { const auto *replacement = get_ascii_replacement(c_str + i); if (replacement) { res.push_back(replacement->ascii); i += strlen(replacement->utf8) - 1; continue; } } if (!isIgnoredChar(ch)) { res.push_back(static_cast(::tolower(ch))); } } return res; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether two names are considered equivalent. * * Two names are equivalent by removing any space, underscore, dash, slash, * { or } character from them, and comparing in a case insensitive way. */ bool Identifier::isEquivalentName(const char *a, const char *b) noexcept { size_t i = 0; size_t j = 0; char lastValidA = 0; char lastValidB = 0; while (a[i] != 0 && b[j] != 0) { char aCh = a[i]; char bCh = b[j]; if (aCh == ' ' && a[i + 1] == '+' && a[i + 2] == ' ') { i += 3; continue; } if (bCh == ' ' && b[j + 1] == '+' && b[j + 2] == ' ') { j += 3; continue; } if (isIgnoredChar(aCh)) { ++i; continue; } if (isIgnoredChar(bCh)) { ++j; continue; } if (aCh == '1' && !(lastValidA >= '0' && lastValidA <= '9') && a[i + 1] == '9' && a[i + 2] >= '0' && a[i + 2] <= '9') { i += 2; lastValidA = '9'; continue; } if (bCh == '1' && !(lastValidB >= '0' && lastValidB <= '9') && b[j + 1] == '9' && b[j + 2] >= '0' && b[j + 2] <= '9') { j += 2; lastValidB = '9'; continue; } if (static_cast(aCh) > 127) { const auto *replacement = get_ascii_replacement(a + i); if (replacement) { aCh = replacement->ascii; i += strlen(replacement->utf8) - 1; } } if (static_cast(bCh) > 127) { const auto *replacement = get_ascii_replacement(b + j); if (replacement) { bCh = replacement->ascii; j += strlen(replacement->utf8) - 1; } } if (::tolower(aCh) != ::tolower(bCh)) { return false; } lastValidA = aCh; lastValidB = bCh; ++i; ++j; } while (a[i] != 0 && isIgnoredChar(a[i])) { ++i; } while (b[j] != 0 && isIgnoredChar(b[j])) { ++j; } return a[i] == b[j]; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct PositionalAccuracy::Private { std::string value_{}; }; //! @endcond // --------------------------------------------------------------------------- PositionalAccuracy::PositionalAccuracy(const std::string &valueIn) : d(internal::make_unique()) { d->value_ = valueIn; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PositionalAccuracy::~PositionalAccuracy() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the value of the positional accuracy. */ const std::string &PositionalAccuracy::value() PROJ_PURE_DEFN { return d->value_; } // --------------------------------------------------------------------------- /** \brief Instantiate a PositionalAccuracy. * * @param valueIn positional accuracy value. * @return a new PositionalAccuracy. */ PositionalAccuracyNNPtr PositionalAccuracy::create(const std::string &valueIn) { return PositionalAccuracy::nn_make_shared(valueIn); } } // namespace metadata NS_PROJ_END proj-6.3.1/src/iso19111/c_api.cpp0000664000175000017500000113546613617003642013175 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: C API wraper of C++ API * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include #include #include #include #include #include #include #include #include #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_internal.h" #include "proj_experimental.h" // clang-format on #include "proj_constants.h" using namespace NS_PROJ::common; using namespace NS_PROJ::crs; using namespace NS_PROJ::cs; using namespace NS_PROJ::datum; using namespace NS_PROJ::io; using namespace NS_PROJ::internal; using namespace NS_PROJ::metadata; using namespace NS_PROJ::operation; using namespace NS_PROJ::util; using namespace NS_PROJ; // --------------------------------------------------------------------------- static void PROJ_NO_INLINE proj_log_error(PJ_CONTEXT *ctx, const char *function, const char *text) { std::string msg(function); msg += ": "; msg += text; ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str()); auto previous_errno = pj_ctx_get_errno(ctx); if (previous_errno == 0) { // only set errno if it wasn't set deeper down the call stack pj_ctx_set_errno(ctx, PJD_ERR_GENERIC_ERROR); } } // --------------------------------------------------------------------------- static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function, const char *text) { std::string msg(function); msg += ": "; msg += text; ctx->logger(ctx->logger_app_data, PJ_LOG_DEBUG, msg.c_str()); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- void proj_context_delete_cpp_context(struct projCppContext *cppContext) { delete cppContext; } // --------------------------------------------------------------------------- projCppContext::projCppContext(PJ_CONTEXT *ctx, const char *dbPath, const std::vector &auxDbPaths) : ctx_(ctx), dbPath_(dbPath ? dbPath : std::string()), auxDbPaths_(auxDbPaths) {} // --------------------------------------------------------------------------- std::vector projCppContext::toVector(const char *const *auxDbPaths) { std::vector res; for (auto iter = auxDbPaths; iter && *iter; ++iter) { res.emplace_back(std::string(*iter)); } return res; } // --------------------------------------------------------------------------- void projCppContext::closeDb() { databaseContext_ = nullptr; } // --------------------------------------------------------------------------- void projCppContext::autoCloseDbIfNeeded() { if (getAutoCloseDb()) { closeDb(); } } // --------------------------------------------------------------------------- NS_PROJ::io::DatabaseContextNNPtr projCppContext::getDatabaseContext() { if (databaseContext_) { return NN_NO_CHECK(databaseContext_); } auto dbContext = NS_PROJ::io::DatabaseContext::create(dbPath_, auxDbPaths_, ctx_); databaseContext_ = dbContext; return dbContext; } // --------------------------------------------------------------------------- static PROJ_NO_INLINE DatabaseContextNNPtr getDBcontext(PJ_CONTEXT *ctx) { if (ctx->cpp_context == nullptr) { ctx->cpp_context = new projCppContext(ctx); } return ctx->cpp_context->getDatabaseContext(); } // --------------------------------------------------------------------------- static PROJ_NO_INLINE DatabaseContextPtr getDBcontextNoException(PJ_CONTEXT *ctx, const char *function) { try { return getDBcontext(ctx).as_nullable(); } catch (const std::exception &e) { proj_log_debug(ctx, function, e.what()); return nullptr; } } // --------------------------------------------------------------------------- static PJ *pj_obj_create(PJ_CONTEXT *ctx, const IdentifiedObjectNNPtr &objIn) { auto coordop = dynamic_cast(objIn.get()); if (coordop) { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { auto formatter = PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, dbContext); auto projString = coordop->exportToPROJString(formatter.get()); auto pj = pj_create_internal(ctx, projString.c_str()); if (pj) { pj->iso_obj = objIn; if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return pj; } } catch (const std::exception &) { // Silence, since we may not always be able to export as a // PROJ string. } } auto pj = pj_new(); if (pj) { pj->ctx = ctx; pj->descr = "ISO-19111 object"; pj->iso_obj = objIn; } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return pj; } //! @endcond // --------------------------------------------------------------------------- /** \brief Opaque object representing a set of operation results. */ struct PJ_OBJ_LIST { //! @cond Doxygen_Suppress std::vector objects; explicit PJ_OBJ_LIST(std::vector &&objectsIn) : objects(std::move(objectsIn)) {} PJ_OBJ_LIST(const PJ_OBJ_LIST &) = delete; PJ_OBJ_LIST &operator=(const PJ_OBJ_LIST &) = delete; //! @endcond }; // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress #define SANITIZE_CTX(ctx) \ do { \ if (ctx == nullptr) { \ ctx = pj_get_default_ctx(); \ } \ } while (0) //! @endcond // --------------------------------------------------------------------------- /** \brief Set if the database must be closed after each C API call where it * has been openeded, and automatically re-openeded when needed. * * The default value is FALSE, that is the database remains open until the * context is destroyed. * * @param ctx PROJ context, or NULL for default context * @param autoclose Boolean parameter * @since 6.2 */ void proj_context_set_autoclose_database(PJ_CONTEXT *ctx, int autoclose) { SANITIZE_CTX(ctx); if (ctx->cpp_context == nullptr) { ctx->cpp_context = new projCppContext(ctx); } ctx->cpp_context->setAutoCloseDb(autoclose != FALSE); } // --------------------------------------------------------------------------- /** \brief Explicitly point to the main PROJ CRS and coordinate operation * definition database ("proj.db"), and potentially auxiliary databases with * same structure. * * @param ctx PROJ context, or NULL for default context * @param dbPath Path to main database, or NULL for default. * @param auxDbPaths NULL-terminated list of auxiliary database filenames, or * NULL. * @param options should be set to NULL for now * @return TRUE in case of success */ int proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath, const char *const *auxDbPaths, const char *const *options) { SANITIZE_CTX(ctx); (void)options; std::string osPrevDbPath; std::vector osPrevAuxDbPaths; bool autoCloseDb = false; if (ctx->cpp_context) { osPrevDbPath = ctx->cpp_context->getDbPath(); osPrevAuxDbPaths = ctx->cpp_context->getAuxDbPaths(); autoCloseDb = ctx->cpp_context->getAutoCloseDb(); } delete ctx->cpp_context; ctx->cpp_context = nullptr; try { ctx->cpp_context = new projCppContext( ctx, dbPath, projCppContext::toVector(auxDbPaths)); ctx->cpp_context->setAutoCloseDb(autoCloseDb); ctx->cpp_context->getDatabaseContext(); ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); delete ctx->cpp_context; ctx->cpp_context = new projCppContext(ctx, osPrevDbPath.c_str(), osPrevAuxDbPaths); ctx->cpp_context->setAutoCloseDb(autoCloseDb); return false; } } // --------------------------------------------------------------------------- /** \brief Returns the path to the database. * * The returned pointer remains valid while ctx is valid, and until * proj_context_set_database_path() is called. * * @param ctx PROJ context, or NULL for default context * @return path, or nullptr */ const char *proj_context_get_database_path(PJ_CONTEXT *ctx) { SANITIZE_CTX(ctx); try { // temporary variable must be used as getDBcontext() might create // ctx->cpp_context auto osPath(getDBcontext(ctx)->getPath()); ctx->cpp_context->lastDbPath_ = osPath; ctx->cpp_context->autoCloseDbIfNeeded(); return ctx->cpp_context->lastDbPath_.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Return a metadata from the database. * * The returned pointer remains valid while ctx is valid, and until * proj_context_get_database_metadata() is called. * * @param ctx PROJ context, or NULL for default context * @param key Metadata key. Must not be NULL * @return value, or nullptr */ const char *proj_context_get_database_metadata(PJ_CONTEXT *ctx, const char *key) { SANITIZE_CTX(ctx); try { // temporary variable must be used as getDBcontext() might create // ctx->cpp_context auto osVal(getDBcontext(ctx)->getMetadata(key)); ctx->cpp_context->lastDbMetadataItem_ = osVal; ctx->cpp_context->autoCloseDbIfNeeded(); return ctx->cpp_context->lastDbMetadataItem_.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Guess the "dialect" of the WKT string. * * @param ctx PROJ context, or NULL for default context * @param wkt String (must not be NULL) */ PJ_GUESSED_WKT_DIALECT proj_context_guess_wkt_dialect(PJ_CONTEXT *ctx, const char *wkt) { (void)ctx; assert(wkt); switch (WKTParser().guessDialect(wkt)) { case WKTParser::WKTGuessedDialect::WKT2_2019: return PJ_GUESSED_WKT2_2019; case WKTParser::WKTGuessedDialect::WKT2_2015: return PJ_GUESSED_WKT2_2015; case WKTParser::WKTGuessedDialect::WKT1_GDAL: return PJ_GUESSED_WKT1_GDAL; case WKTParser::WKTGuessedDialect::WKT1_ESRI: return PJ_GUESSED_WKT1_ESRI; case WKTParser::WKTGuessedDialect::NOT_WKT: break; } return PJ_GUESSED_NOT_WKT; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const char *getOptionValue(const char *option, const char *keyWithEqual) noexcept { if (ci_starts_with(option, keyWithEqual)) { return option + strlen(keyWithEqual); } return nullptr; } //! @endcond // --------------------------------------------------------------------------- /** \brief "Clone" an object. * * Technically this just increases the reference counter on the object, since * PJ objects are immutable. * * The returned object must be unreferenced with proj_destroy() after use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object to clone. Must not be NULL. * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); if (!obj->iso_obj) { return nullptr; } try { return pj_obj_create(ctx, NN_NO_CHECK(obj->iso_obj)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate an object from a WKT string, PROJ string, object code * (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326", * "urn:ogc:def:coordinateOperation:EPSG::1671") or PROJJSON string. * * This function calls osgeo::proj::io::createFromUserInput() * * The returned object must be unreferenced with proj_destroy() after use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param text String (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ PJ *proj_create(PJ_CONTEXT *ctx, const char *text) { SANITIZE_CTX(ctx); assert(text); // Only connect to proj.db if needed if (strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr) { getDBcontextNoException(ctx, __FUNCTION__); } try { auto identifiedObject = nn_dynamic_pointer_cast( createFromUserInput(text, ctx)); if (identifiedObject) { return pj_obj_create(ctx, NN_NO_CHECK(identifiedObject)); } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } // --------------------------------------------------------------------------- template static PROJ_STRING_LIST to_string_list(T &&set) { auto ret = new char *[set.size() + 1]; size_t i = 0; for (const auto &str : set) { try { ret[i] = new char[str.size() + 1]; } catch (const std::exception &) { while (--i > 0) { delete[] ret[i]; } delete[] ret; throw; } std::memcpy(ret[i], str.c_str(), str.size() + 1); i++; } ret[i] = nullptr; return ret; } // --------------------------------------------------------------------------- /** \brief Instantiate an object from a WKT string. * * This function calls osgeo::proj::io::WKTParser::createFromWKT() * * The returned object must be unreferenced with proj_destroy() after use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param wkt WKT string (must not be NULL) * @param options null-terminated list of options, or NULL. Currently * supported options are: *
    *
  • STRICT=YES/NO. Defaults to NO. When set to YES, strict validation will * be enabled.
  • *
* @param out_warnings Pointer to a PROJ_STRING_LIST object, or NULL. * If provided, *out_warnings will contain a list of warnings, typically for * non recognized projection method or parameters. It must be freed with * proj_string_list_destroy(). * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL. * If provided, *out_grammar_errors will contain a list of errors regarding the * WKT grammar. It must be freed with proj_string_list_destroy(). * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt, const char *const *options, PROJ_STRING_LIST *out_warnings, PROJ_STRING_LIST *out_grammar_errors) { SANITIZE_CTX(ctx); assert(wkt); if (out_warnings) { *out_warnings = nullptr; } if (out_grammar_errors) { *out_grammar_errors = nullptr; } try { WKTParser parser; auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); if (dbContext) { parser.attachDatabaseContext(NN_NO_CHECK(dbContext)); } parser.setStrict(false); for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "STRICT="))) { parser.setStrict(ci_equal(value, "YES")); } else { std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); return nullptr; } } auto obj = nn_dynamic_pointer_cast( parser.createFromWKT(wkt)); std::vector warningsFromParsing; if (out_grammar_errors) { auto rawWarnings = parser.warningList(); std::vector grammarWarnings; for (const auto &msg : rawWarnings) { if (msg.find("Default it to") != std::string::npos) { warningsFromParsing.push_back(msg); } else { grammarWarnings.push_back(msg); } } if (!grammarWarnings.empty()) { *out_grammar_errors = to_string_list(grammarWarnings); } } if (obj && out_warnings) { auto derivedCRS = dynamic_cast(obj.get()); if (derivedCRS) { auto warnings = derivedCRS->derivingConversionRef()->validateParameters(); warnings.insert(warnings.end(), warningsFromParsing.begin(), warningsFromParsing.end()); if (!warnings.empty()) { *out_warnings = to_string_list(warnings); } } else { auto singleOp = dynamic_cast(obj.get()); if (singleOp) { auto warnings = singleOp->validateParameters(); if (!warnings.empty()) { *out_warnings = to_string_list(warnings); } } } } if (obj) { return pj_obj_create(ctx, NN_NO_CHECK(obj)); } } catch (const std::exception &e) { if (out_grammar_errors) { std::list exc{e.what()}; try { *out_grammar_errors = to_string_list(exc); } catch (const std::exception &) { proj_log_error(ctx, __FUNCTION__, e.what()); } } else { proj_log_error(ctx, __FUNCTION__, e.what()); } } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate an object from a database lookup. * * The returned object must be unreferenced with proj_destroy() after use. * It should be used by at most one thread at a time. * * @param ctx Context, or NULL for default context. * @param auth_name Authority name (must not be NULL) * @param code Object code (must not be NULL) * @param category Object category * @param usePROJAlternativeGridNames Whether PROJ alternative grid names * should be substituted to the official grid names. Only used on * transformations * @param options should be set to NULL for now * @return Object that must be unreferenced with proj_destroy(), or NULL in * case of error. */ PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name, const char *code, PJ_CATEGORY category, int usePROJAlternativeGridNames, const char *const *options) { assert(auth_name); assert(code); (void)options; SANITIZE_CTX(ctx); try { const std::string codeStr(code); auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name); IdentifiedObjectPtr obj; switch (category) { case PJ_CATEGORY_ELLIPSOID: obj = factory->createEllipsoid(codeStr).as_nullable(); break; case PJ_CATEGORY_PRIME_MERIDIAN: obj = factory->createPrimeMeridian(codeStr).as_nullable(); break; case PJ_CATEGORY_DATUM: obj = factory->createDatum(codeStr).as_nullable(); break; case PJ_CATEGORY_CRS: obj = factory->createCoordinateReferenceSystem(codeStr).as_nullable(); break; case PJ_CATEGORY_COORDINATE_OPERATION: obj = factory ->createCoordinateOperation( codeStr, usePROJAlternativeGridNames != 0) .as_nullable(); break; } return pj_obj_create(ctx, NN_NO_CHECK(obj)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const char *get_unit_category(UnitOfMeasure::Type type) { const char *ret = nullptr; switch (type) { case UnitOfMeasure::Type::UNKNOWN: ret = "unknown"; break; case UnitOfMeasure::Type::NONE: ret = "none"; break; case UnitOfMeasure::Type::ANGULAR: ret = "angular"; break; case UnitOfMeasure::Type::LINEAR: ret = "linear"; break; case UnitOfMeasure::Type::SCALE: ret = "scale"; break; case UnitOfMeasure::Type::TIME: ret = "time"; break; case UnitOfMeasure::Type::PARAMETRIC: ret = "parametric"; break; } return ret; } //! @endcond // --------------------------------------------------------------------------- /** \brief Get information for a unit of measure from a database lookup. * * @param ctx Context, or NULL for default context. * @param auth_name Authority name (must not be NULL) * @param code Unit of measure code (must not be NULL) * @param out_name Pointer to a string value to store the parameter name. or * NULL. This value remains valid until the next call to * proj_uom_get_info_from_database() or the context destruction. * @param out_conv_factor Pointer to a value to store the conversion * factor of the prime meridian longitude unit to radian. or NULL * @param out_category Pointer to a string value to store the parameter name. or * NULL. This value might be "unknown", "none", "linear", "angular", "scale", * "time" or "parametric"; * @return TRUE in case of success */ int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name, const char *code, const char **out_name, double *out_conv_factor, const char **out_category) { assert(auth_name); assert(code); SANITIZE_CTX(ctx); try { auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name); auto obj = factory->createUnitOfMeasure(code); if (out_name) { ctx->cpp_context->lastUOMName_ = obj->name(); *out_name = ctx->cpp_context->lastUOMName_.c_str(); } if (out_conv_factor) { *out_conv_factor = obj->conversionToSI(); } if (out_category) { *out_category = get_unit_category(obj->type()); } ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return false; } // --------------------------------------------------------------------------- /** \brief Get information for a grid from a database lookup. * * @param ctx Context, or NULL for default context. * @param grid_name Grid name (must not be NULL) * @param out_full_name Pointer to a string value to store the grid full * filename. or NULL * @param out_package_name Pointer to a string value to store the package name * where * the grid might be found. or NULL * @param out_url Pointer to a string value to store the grid URL or the * package URL where the grid might be found. or NULL * @param out_direct_download Pointer to a int (boolean) value to store whether * *out_url can be downloaded directly. or NULL * @param out_open_license Pointer to a int (boolean) value to store whether * the grid is released with an open license. or NULL * @param out_available Pointer to a int (boolean) value to store whether the * grid is available at runtime. or NULL * @return TRUE in case of success. */ int PROJ_DLL proj_grid_get_info_from_database( PJ_CONTEXT *ctx, const char *grid_name, const char **out_full_name, const char **out_package_name, const char **out_url, int *out_direct_download, int *out_open_license, int *out_available) { assert(grid_name); SANITIZE_CTX(ctx); try { auto db_context = getDBcontext(ctx); bool direct_download; bool open_license; bool available; if (!db_context->lookForGridInfo( grid_name, ctx->cpp_context->lastGridFullName_, ctx->cpp_context->lastGridPackageName_, ctx->cpp_context->lastGridUrl_, direct_download, open_license, available)) { ctx->cpp_context->autoCloseDbIfNeeded(); return false; } if (out_full_name) *out_full_name = ctx->cpp_context->lastGridFullName_.c_str(); if (out_package_name) *out_package_name = ctx->cpp_context->lastGridPackageName_.c_str(); if (out_url) *out_url = ctx->cpp_context->lastGridUrl_.c_str(); if (out_direct_download) *out_direct_download = direct_download ? 1 : 0; if (out_open_license) *out_open_license = open_license ? 1 : 0; if (out_available) *out_available = available ? 1 : 0; ctx->cpp_context->autoCloseDbIfNeeded(); return true; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return false; } // --------------------------------------------------------------------------- /** \brief Return GeodeticCRS that use the specified datum. * * @param ctx Context, or NULL for default context. * @param crs_auth_name CRS authority name, or NULL. * @param datum_auth_name Datum authority name (must not be NULL) * @param datum_code Datum code (must not be NULL) * @param crs_type "geographic 2D", "geographic 3D", "geocentric" or NULL * @return a result set that must be unreferenced with * proj_list_destroy(), or NULL in case of error. */ PJ_OBJ_LIST *proj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_auth_name, const char *datum_auth_name, const char *datum_code, const char *crs_type) { assert(datum_auth_name); assert(datum_code); SANITIZE_CTX(ctx); try { auto factory = AuthorityFactory::create( getDBcontext(ctx), crs_auth_name ? crs_auth_name : ""); auto res = factory->createGeodeticCRSFromDatum( datum_auth_name, datum_code, crs_type ? crs_type : ""); std::vector objects; for (const auto &obj : res) { objects.push_back(obj); } ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static AuthorityFactory::ObjectType convertPJObjectTypeToObjectType(PJ_TYPE type, bool &valid) { valid = true; AuthorityFactory::ObjectType cppType = AuthorityFactory::ObjectType::CRS; switch (type) { case PJ_TYPE_ELLIPSOID: cppType = AuthorityFactory::ObjectType::ELLIPSOID; break; case PJ_TYPE_PRIME_MERIDIAN: cppType = AuthorityFactory::ObjectType::PRIME_MERIDIAN; break; case PJ_TYPE_GEODETIC_REFERENCE_FRAME: case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME: cppType = AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME; break; case PJ_TYPE_VERTICAL_REFERENCE_FRAME: case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME: cppType = AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME; break; case PJ_TYPE_DATUM_ENSEMBLE: cppType = AuthorityFactory::ObjectType::DATUM; break; case PJ_TYPE_CRS: cppType = AuthorityFactory::ObjectType::CRS; break; case PJ_TYPE_GEODETIC_CRS: cppType = AuthorityFactory::ObjectType::GEODETIC_CRS; break; case PJ_TYPE_GEOCENTRIC_CRS: cppType = AuthorityFactory::ObjectType::GEOCENTRIC_CRS; break; case PJ_TYPE_GEOGRAPHIC_CRS: cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_CRS; break; case PJ_TYPE_GEOGRAPHIC_2D_CRS: cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS; break; case PJ_TYPE_GEOGRAPHIC_3D_CRS: cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS; break; case PJ_TYPE_VERTICAL_CRS: cppType = AuthorityFactory::ObjectType::VERTICAL_CRS; break; case PJ_TYPE_PROJECTED_CRS: cppType = AuthorityFactory::ObjectType::PROJECTED_CRS; break; case PJ_TYPE_COMPOUND_CRS: cppType = AuthorityFactory::ObjectType::COMPOUND_CRS; break; case PJ_TYPE_ENGINEERING_CRS: valid = false; break; case PJ_TYPE_TEMPORAL_CRS: valid = false; break; case PJ_TYPE_BOUND_CRS: valid = false; break; case PJ_TYPE_OTHER_CRS: cppType = AuthorityFactory::ObjectType::CRS; break; case PJ_TYPE_CONVERSION: cppType = AuthorityFactory::ObjectType::CONVERSION; break; case PJ_TYPE_TRANSFORMATION: cppType = AuthorityFactory::ObjectType::TRANSFORMATION; break; case PJ_TYPE_CONCATENATED_OPERATION: cppType = AuthorityFactory::ObjectType::CONCATENATED_OPERATION; break; case PJ_TYPE_OTHER_COORDINATE_OPERATION: cppType = AuthorityFactory::ObjectType::COORDINATE_OPERATION; break; case PJ_TYPE_UNKNOWN: valid = false; break; } return cppType; } //! @endcond // --------------------------------------------------------------------------- /** \brief Return a list of objects by their name. * * @param ctx Context, or NULL for default context. * @param auth_name Authority name, used to restrict the search. * Or NULL for all authorities. * @param searchedName Searched name. Must be at least 2 character long. * @param types List of object types into which to search. If * NULL, all object types will be searched. * @param typesCount Number of elements in types, or 0 if types is NULL * @param approximateMatch Whether approximate name identification is allowed. * @param limitResultCount Maximum number of results to return. * Or 0 for unlimited. * @param options should be set to NULL for now * @return a result set that must be unreferenced with * proj_list_destroy(), or NULL in case of error. */ PJ_OBJ_LIST *proj_create_from_name(PJ_CONTEXT *ctx, const char *auth_name, const char *searchedName, const PJ_TYPE *types, size_t typesCount, int approximateMatch, size_t limitResultCount, const char *const *options) { assert(searchedName); assert((types != nullptr && typesCount > 0) || (types == nullptr && typesCount == 0)); (void)options; SANITIZE_CTX(ctx); try { auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name ? auth_name : ""); std::vector allowedTypes; for (size_t i = 0; i < typesCount; ++i) { bool valid = false; auto type = convertPJObjectTypeToObjectType(types[i], valid); if (valid) { allowedTypes.push_back(type); } } auto res = factory->createObjectsFromName(searchedName, allowedTypes, approximateMatch != 0, limitResultCount); std::vector objects; for (const auto &obj : res) { objects.push_back(obj); } ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- /** \brief Return the type of an object. * * @param obj Object (must not be NULL) * @return its type. */ PJ_TYPE proj_get_type(const PJ *obj) { assert(obj); if (!obj->iso_obj) { return PJ_TYPE_UNKNOWN; } auto ptr = obj->iso_obj.get(); if (dynamic_cast(ptr)) { return PJ_TYPE_ELLIPSOID; } if (dynamic_cast(ptr)) { return PJ_TYPE_PRIME_MERIDIAN; } if (dynamic_cast(ptr)) { return PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME; } if (dynamic_cast(ptr)) { return PJ_TYPE_GEODETIC_REFERENCE_FRAME; } if (dynamic_cast(ptr)) { return PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME; } if (dynamic_cast(ptr)) { return PJ_TYPE_VERTICAL_REFERENCE_FRAME; } if (dynamic_cast(ptr)) { return PJ_TYPE_DATUM_ENSEMBLE; } { auto crs = dynamic_cast(ptr); if (crs) { if (crs->coordinateSystem()->axisList().size() == 2) { return PJ_TYPE_GEOGRAPHIC_2D_CRS; } else { return PJ_TYPE_GEOGRAPHIC_3D_CRS; } } } { auto crs = dynamic_cast(ptr); if (crs) { if (crs->isGeocentric()) { return PJ_TYPE_GEOCENTRIC_CRS; } else { return PJ_TYPE_GEODETIC_CRS; } } } if (dynamic_cast(ptr)) { return PJ_TYPE_VERTICAL_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_PROJECTED_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_COMPOUND_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_TEMPORAL_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_ENGINEERING_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_BOUND_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_OTHER_CRS; } if (dynamic_cast(ptr)) { return PJ_TYPE_CONVERSION; } if (dynamic_cast(ptr)) { return PJ_TYPE_TRANSFORMATION; } if (dynamic_cast(ptr)) { return PJ_TYPE_CONCATENATED_OPERATION; } if (dynamic_cast(ptr)) { return PJ_TYPE_OTHER_COORDINATE_OPERATION; } return PJ_TYPE_UNKNOWN; } // --------------------------------------------------------------------------- /** \brief Return whether an object is deprecated. * * @param obj Object (must not be NULL) * @return TRUE if it is deprecated, FALSE otherwise */ int proj_is_deprecated(const PJ *obj) { assert(obj); if (!obj->iso_obj) { return false; } return obj->iso_obj->isDeprecated(); } // --------------------------------------------------------------------------- /** \brief Return a list of non-deprecated objects related to the passed one * * @param ctx Context, or NULL for default context. * @param obj Object (of type CRS for now) for which non-deprecated objects * must be searched. Must not be NULL * @return a result set that must be unreferenced with * proj_list_destroy(), or NULL in case of error. */ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) { assert(obj); SANITIZE_CTX(ctx); auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { return nullptr; } try { std::vector objects; auto res = crs->getNonDeprecated(getDBcontext(ctx)); for (const auto &resObj : res) { objects.push_back(resObj); } ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj, const PJ *other, PJ_COMPARISON_CRITERION criterion) { assert(obj); assert(other); if (!obj->iso_obj) { return false; } if (!other->iso_obj) { return false; } const auto cppCriterion = ([](PJ_COMPARISON_CRITERION l_criterion) { switch (l_criterion) { case PJ_COMP_STRICT: return IComparable::Criterion::STRICT; case PJ_COMP_EQUIVALENT: return IComparable::Criterion::EQUIVALENT; case PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS: break; } return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS; })(criterion); int res = obj->iso_obj->isEquivalentTo( other->iso_obj.get(), cppCriterion, ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx") : nullptr); if (ctx && ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return res; } // --------------------------------------------------------------------------- /** \brief Return whether two objects are equivalent. * * Use proj_is_equivalent_to_with_ctx() to be able to use database information. * * @param obj Object (must not be NULL) * @param other Other object (must not be NULL) * @param criterion Comparison criterion * @return TRUE if they are equivalent */ int proj_is_equivalent_to(const PJ *obj, const PJ *other, PJ_COMPARISON_CRITERION criterion) { return proj_is_equivalent_to_internal(nullptr, obj, other, criterion); } // --------------------------------------------------------------------------- /** \brief Return whether two objects are equivalent * * Possibly using database to check for name aliases. * * @param ctx PROJ context, or NULL for default context * @param obj Object (must not be NULL) * @param other Other object (must not be NULL) * @param criterion Comparison criterion * @return TRUE if they are equivalent * @since 6.3 */ int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj, const PJ *other, PJ_COMPARISON_CRITERION criterion) { SANITIZE_CTX(ctx); return proj_is_equivalent_to_internal(ctx, obj, other, criterion); } // --------------------------------------------------------------------------- /** \brief Return whether an object is a CRS * * @param obj Object (must not be NULL) */ int proj_is_crs(const PJ *obj) { assert(obj); return dynamic_cast(obj->iso_obj.get()) != nullptr; } // --------------------------------------------------------------------------- /** \brief Get the name of an object. * * The lifetime of the returned string is the same as the input obj parameter. * * @param obj Object (must not be NULL) * @return a string, or NULL in case of error or missing name. */ const char *proj_get_name(const PJ *obj) { assert(obj); if (!obj->iso_obj) { return nullptr; } const auto &desc = obj->iso_obj->name()->description(); if (!desc.has_value()) { return nullptr; } // The object will still be alive after the function call. // cppcheck-suppress stlcstr return desc->c_str(); } // --------------------------------------------------------------------------- /** \brief Get the remarks of an object. * * The lifetime of the returned string is the same as the input obj parameter. * * @param obj Object (must not be NULL) * @return a string, or NULL in case of error. */ const char *proj_get_remarks(const PJ *obj) { assert(obj); if (!obj->iso_obj) { return nullptr; } // The object will still be alive after the function call. // cppcheck-suppress stlcstr return obj->iso_obj->remarks().c_str(); } // --------------------------------------------------------------------------- /** \brief Get the authority name / codespace of an identifier of an object. * * The lifetime of the returned string is the same as the input obj parameter. * * @param obj Object (must not be NULL) * @param index Index of the identifier. 0 = first identifier * @return a string, or NULL in case of error or missing name. */ const char *proj_get_id_auth_name(const PJ *obj, int index) { assert(obj); if (!obj->iso_obj) { return nullptr; } const auto &ids = obj->iso_obj->identifiers(); if (static_cast(index) >= ids.size()) { return nullptr; } const auto &codeSpace = ids[index]->codeSpace(); if (!codeSpace.has_value()) { return nullptr; } // The object will still be alive after the function call. // cppcheck-suppress stlcstr return codeSpace->c_str(); } // --------------------------------------------------------------------------- /** \brief Get the code of an identifier of an object. * * The lifetime of the returned string is the same as the input obj parameter. * * @param obj Object (must not be NULL) * @param index Index of the identifier. 0 = first identifier * @return a string, or NULL in case of error or missing name. */ const char *proj_get_id_code(const PJ *obj, int index) { assert(obj); if (!obj->iso_obj) { return nullptr; } const auto &ids = obj->iso_obj->identifiers(); if (static_cast(index) >= ids.size()) { return nullptr; } return ids[index]->code().c_str(); } // --------------------------------------------------------------------------- /** \brief Get a WKT representation of an object. * * The returned string is valid while the input obj parameter is valid, * and until a next call to proj_as_wkt() with the same input object. * * This function calls osgeo::proj::io::IWKTExportable::exportToWKT(). * * This function may return NULL if the object is not compatible with an * export to the requested type. * * @param ctx PROJ context, or NULL for default context * @param obj Object (must not be NULL) * @param type WKT version. * @param options null-terminated list of options, or NULL. Currently * supported options are: *
    *
  • MULTILINE=YES/NO. Defaults to YES, except for WKT1_ESRI
  • *
  • INDENTATION_WIDTH=number. Defauls to 4 (when multiline output is * on).
  • *
  • OUTPUT_AXIS=AUTO/YES/NO. In AUTO mode, axis will be output for WKT2 * variants, for WKT1_GDAL for ProjectedCRS with easting/northing ordering * (otherwise stripped), but not for WKT1_ESRI. Setting to YES will output * them unconditionally, and to NO will omit them unconditionally.
  • *
* @return a string, or NULL in case of error. */ const char *proj_as_wkt(PJ_CONTEXT *ctx, const PJ *obj, PJ_WKT_TYPE type, const char *const *options) { SANITIZE_CTX(ctx); assert(obj); if (!obj->iso_obj) { return nullptr; } const auto convention = ([](PJ_WKT_TYPE l_type) { switch (l_type) { case PJ_WKT2_2015: return WKTFormatter::Convention::WKT2_2015; case PJ_WKT2_2015_SIMPLIFIED: return WKTFormatter::Convention::WKT2_2015_SIMPLIFIED; case PJ_WKT2_2019: return WKTFormatter::Convention::WKT2_2019; case PJ_WKT2_2019_SIMPLIFIED: return WKTFormatter::Convention::WKT2_2019_SIMPLIFIED; case PJ_WKT1_GDAL: return WKTFormatter::Convention::WKT1_GDAL; case PJ_WKT1_ESRI: break; } return WKTFormatter::Convention::WKT1_ESRI; })(type); try { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); auto formatter = WKTFormatter::create(convention, dbContext); for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "MULTILINE="))) { formatter->setMultiLine(ci_equal(value, "YES")); } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) { formatter->setIndentationWidth(std::atoi(value)); } else if ((value = getOptionValue(*iter, "OUTPUT_AXIS="))) { if (!ci_equal(value, "AUTO")) { formatter->setOutputAxis( ci_equal(value, "YES") ? WKTFormatter::OutputAxisRule::YES : WKTFormatter::OutputAxisRule::NO); } } else if ((value = getOptionValue(*iter, "STRICT="))) { formatter->setStrict(ci_equal(value, "YES")); } else { std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } obj->lastWKT = obj->iso_obj->exportToWKT(formatter.get()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return obj->lastWKT.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Get a PROJ string representation of an object. * * The returned string is valid while the input obj parameter is valid, * and until a next call to proj_as_proj_string() with the same input * object. * * This function calls * osgeo::proj::io::IPROJStringExportable::exportToPROJString(). * * This function may return NULL if the object is not compatible with an * export to the requested type. * * @param ctx PROJ context, or NULL for default context * @param obj Object (must not be NULL) * @param type PROJ String version. * @param options NULL-terminated list of strings with "KEY=VALUE" format. or * NULL. * The currently recognized option is USE_APPROX_TMERC=YES to add the +approx * flag to +proj=tmerc or +proj=utm * @return a string, or NULL in case of error. */ const char *proj_as_proj_string(PJ_CONTEXT *ctx, const PJ *obj, PJ_PROJ_STRING_TYPE type, const char *const *options) { SANITIZE_CTX(ctx); assert(obj); auto exportable = dynamic_cast(obj->iso_obj.get()); if (!exportable) { proj_log_error(ctx, __FUNCTION__, "Object type not exportable to PROJ"); return nullptr; } // Make sure that the C and C++ enumeration match static_assert(static_cast(PJ_PROJ_5) == static_cast(PROJStringFormatter::Convention::PROJ_5), ""); static_assert(static_cast(PJ_PROJ_4) == static_cast(PROJStringFormatter::Convention::PROJ_4), ""); // Make sure we enumerate all values. If adding a new value, as we // don't have a default clause, the compiler will warn. switch (type) { case PJ_PROJ_5: case PJ_PROJ_4: break; } const PROJStringFormatter::Convention convention = static_cast(type); auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { auto formatter = PROJStringFormatter::create(convention, dbContext); if (options != nullptr && options[0] != nullptr) { if (ci_equal(options[0], "USE_APPROX_TMERC=YES")) { formatter->setUseApproxTMerc(true); } } obj->lastPROJString = exportable->exportToPROJString(formatter.get()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return obj->lastPROJString.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Get a PROJJSON string representation of an object. * * The returned string is valid while the input obj parameter is valid, * and until a next call to proj_as_proj_string() with the same input * object. * * This function calls * osgeo::proj::io::IJSONExportable::exportToJSON(). * * This function may return NULL if the object is not compatible with an * export to the requested type. * * @param ctx PROJ context, or NULL for default context * @param obj Object (must not be NULL) * @param options NULL-terminated list of strings with "KEY=VALUE" format. or * NULL. Currently * supported options are: *
    *
  • MULTILINE=YES/NO. Defaults to YES
  • *
  • INDENTATION_WIDTH=number. Defauls to 2 (when multiline output is * on).
  • *
  • SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to * disable it.
  • *
* @return a string, or NULL in case of error. * * @since 6.2 */ const char *proj_as_projjson(PJ_CONTEXT *ctx, const PJ *obj, const char *const *options) { SANITIZE_CTX(ctx); assert(obj); auto exportable = dynamic_cast(obj->iso_obj.get()); if (!exportable) { proj_log_error(ctx, __FUNCTION__, "Object type not exportable to JSON"); return nullptr; } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { auto formatter = JSONFormatter::create(dbContext); for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "MULTILINE="))) { formatter->setMultiLine(ci_equal(value, "YES")); } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) { formatter->setIndentationWidth(std::atoi(value)); } else if ((value = getOptionValue(*iter, "SCHEMA="))) { formatter->setSchema(value); } else { std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); return nullptr; } } obj->lastJSONString = exportable->exportToJSON(formatter.get()); return obj->lastJSONString.c_str(); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Get the scope of an object. * * In case of multiple usages, this will be the one of first usage. * * The lifetime of the returned string is the same as the input obj parameter. * * @param obj Object (must not be NULL) * @return a string, or NULL in case of error or missing scope. */ const char *proj_get_scope(const PJ *obj) { assert(obj); if (!obj->iso_obj) { return nullptr; } auto objectUsage = dynamic_cast(obj->iso_obj.get()); if (!objectUsage) { return nullptr; } const auto &domains = objectUsage->domains(); if (domains.empty()) { return nullptr; } const auto &scope = domains[0]->scope(); if (!scope.has_value()) { return nullptr; } // The object will still be alive after the function call. // cppcheck-suppress stlcstr return scope->c_str(); } // --------------------------------------------------------------------------- /** \brief Return the area of use of an object. * * In case of multiple usages, this will be the one of first usage. * * @param ctx PROJ context, or NULL for default context * @param obj Object (must not be NULL) * @param out_west_lon_degree Pointer to a double to receive the west longitude * (in degrees). Or NULL. If the returned value is -1000, the bounding box is * unknown. * @param out_south_lat_degree Pointer to a double to receive the south latitude * (in degrees). Or NULL. If the returned value is -1000, the bounding box is * unknown. * @param out_east_lon_degree Pointer to a double to receive the east longitude * (in degrees). Or NULL. If the returned value is -1000, the bounding box is * unknown. * @param out_north_lat_degree Pointer to a double to receive the north latitude * (in degrees). Or NULL. If the returned value is -1000, the bounding box is * unknown. * @param out_area_name Pointer to a string to receive the name of the area of * use. Or NULL. *p_area_name is valid while obj is valid itself. * @return TRUE in case of success, FALSE in case of error or if the area * of use is unknown. */ int proj_get_area_of_use(PJ_CONTEXT *ctx, const PJ *obj, double *out_west_lon_degree, double *out_south_lat_degree, double *out_east_lon_degree, double *out_north_lat_degree, const char **out_area_name) { (void)ctx; if (out_area_name) { *out_area_name = nullptr; } auto objectUsage = dynamic_cast(obj->iso_obj.get()); if (!objectUsage) { return false; } const auto &domains = objectUsage->domains(); if (domains.empty()) { return false; } const auto &extent = domains[0]->domainOfValidity(); if (!extent) { return false; } const auto &desc = extent->description(); if (desc.has_value() && out_area_name) { *out_area_name = desc->c_str(); } const auto &geogElements = extent->geographicElements(); if (!geogElements.empty()) { auto bbox = dynamic_cast(geogElements[0].get()); if (bbox) { if (out_west_lon_degree) { *out_west_lon_degree = bbox->westBoundLongitude(); } if (out_south_lat_degree) { *out_south_lat_degree = bbox->southBoundLatitude(); } if (out_east_lon_degree) { *out_east_lon_degree = bbox->eastBoundLongitude(); } if (out_north_lat_degree) { *out_north_lat_degree = bbox->northBoundLatitude(); } return true; } } if (out_west_lon_degree) { *out_west_lon_degree = -1000; } if (out_south_lat_degree) { *out_south_lat_degree = -1000; } if (out_east_lon_degree) { *out_east_lon_degree = -1000; } if (out_north_lat_degree) { *out_north_lat_degree = -1000; } return true; } // --------------------------------------------------------------------------- static const GeodeticCRS *extractGeodeticCRS(PJ_CONTEXT *ctx, const PJ *crs, const char *fname) { assert(crs); auto l_crs = dynamic_cast(crs->iso_obj.get()); if (!l_crs) { proj_log_error(ctx, fname, "Object is not a CRS"); return nullptr; } auto geodCRS = l_crs->extractGeodeticCRSRaw(); if (!geodCRS) { proj_log_error(ctx, fname, "CRS has no geodetic CRS"); } return geodCRS; } // --------------------------------------------------------------------------- /** \brief Get the geodeticCRS / geographicCRS from a CRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type CRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_get_geodetic_crs(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__); if (!geodCRS) { return nullptr; } return pj_obj_create(ctx, NN_NO_CHECK(nn_dynamic_pointer_cast( geodCRS->shared_from_this()))); } // --------------------------------------------------------------------------- /** \brief Get a CRS component from a CompoundCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type CRS (must not be NULL) * @param index Index of the CRS component (typically 0 = horizontal, 1 = * vertical) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_get_sub_crs(PJ_CONTEXT *ctx, const PJ *crs, int index) { SANITIZE_CTX(ctx); assert(crs); auto l_crs = dynamic_cast(crs->iso_obj.get()); if (!l_crs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CompoundCRS"); return nullptr; } const auto &components = l_crs->componentReferenceSystems(); if (static_cast(index) >= components.size()) { return nullptr; } return pj_obj_create(ctx, components[index]); } // --------------------------------------------------------------------------- /** \brief Returns a BoundCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param base_crs Base CRS (must not be NULL) * @param hub_crs Hub CRS (must not be NULL) * @param transformation Transformation (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ *base_crs, const PJ *hub_crs, const PJ *transformation) { SANITIZE_CTX(ctx); assert(base_crs); assert(hub_crs); assert(transformation); auto l_base_crs = std::dynamic_pointer_cast(base_crs->iso_obj); if (!l_base_crs) { proj_log_error(ctx, __FUNCTION__, "base_crs is not a CRS"); return nullptr; } auto l_hub_crs = std::dynamic_pointer_cast(hub_crs->iso_obj); if (!l_hub_crs) { proj_log_error(ctx, __FUNCTION__, "hub_crs is not a CRS"); return nullptr; } auto l_transformation = std::dynamic_pointer_cast(transformation->iso_obj); if (!l_transformation) { proj_log_error(ctx, __FUNCTION__, "transformation is not a CRS"); return nullptr; } try { return pj_obj_create(ctx, BoundCRS::create(NN_NO_CHECK(l_base_crs), NN_NO_CHECK(l_hub_crs), NN_NO_CHECK(l_transformation))); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Returns potentially * a BoundCRS, with a transformation to EPSG:4326, wrapping this CRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * This is the same as method * osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible() * * @param ctx PROJ context, or NULL for default context * @param crs Object of type CRS (must not be NULL) * @param options null-terminated list of options, or NULL. Currently * supported options are: *
    *
  • ALLOW_INTERMEDIATE_CRS=ALWAYS/IF_NO_DIRECT_TRANSFORMATION/NEVER. Defaults * to NEVER. When set to ALWAYS/IF_NO_DIRECT_TRANSFORMATION, * intermediate CRS may be considered when computing the possible * transformations. Slower.
  • *
* @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs, const char *const *options) { SANITIZE_CTX(ctx); assert(crs); auto l_crs = dynamic_cast(crs->iso_obj.get()); if (!l_crs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CRS"); return nullptr; } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { CoordinateOperationContext::IntermediateCRSUse allowIntermediateCRS = CoordinateOperationContext::IntermediateCRSUse::NEVER; for (auto iter = options; iter && iter[0]; ++iter) { const char *value; if ((value = getOptionValue(*iter, "ALLOW_INTERMEDIATE_CRS="))) { if (ci_equal(value, "YES") || ci_equal(value, "ALWAYS")) { allowIntermediateCRS = CoordinateOperationContext::IntermediateCRSUse::ALWAYS; } else if (ci_equal(value, "IF_NO_DIRECT_TRANSFORMATION")) { allowIntermediateCRS = CoordinateOperationContext:: IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION; } } else { std::string msg("Unknown option :"); msg += *iter; proj_log_error(ctx, __FUNCTION__, msg.c_str()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } return pj_obj_create(ctx, l_crs->createBoundCRSToWGS84IfPossible( dbContext, allowIntermediateCRS)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Returns a BoundCRS, with a transformation to a hub geographic 3D crs * (use EPSG:4979 for WGS84 for example), using a grid. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param vert_crs Object of type VerticalCRS (must not be NULL) * @param hub_geographic_3D_crs Object of type Geographic 3D CRS (must not be * NULL) * @param grid_name Grid name (typically a .gtx file) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. * @since 6.3 */ PJ *proj_crs_create_bound_vertical_crs(PJ_CONTEXT *ctx, const PJ *vert_crs, const PJ *hub_geographic_3D_crs, const char *grid_name) { SANITIZE_CTX(ctx); assert(vert_crs); assert(hub_geographic_3D_crs); assert(grid_name); auto l_crs = std::dynamic_pointer_cast(vert_crs->iso_obj); if (!l_crs) { proj_log_error(ctx, __FUNCTION__, "vert_crs is not a VerticalCRS"); return nullptr; } auto hub_crs = std::dynamic_pointer_cast(hub_geographic_3D_crs->iso_obj); if (!hub_crs) { proj_log_error(ctx, __FUNCTION__, "hub_geographic_3D_crs is not a CRS"); return nullptr; } try { auto nnCRS = NN_NO_CHECK(l_crs); auto nnHubCRS = NN_NO_CHECK(hub_crs); auto transformation = Transformation::createGravityRelatedHeightToGeographic3D( PropertyMap().set(IdentifiedObject::NAME_KEY, "unknown to " + hub_crs->nameStr() + " ellipsoidal height"), nnCRS, nnHubCRS, nullptr, std::string(grid_name), std::vector()); return pj_obj_create(ctx, BoundCRS::create(nnCRS, nnHubCRS, transformation)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Get the ellipsoid from a CRS or a GeodeticReferenceFrame. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); auto ptr = obj->iso_obj.get(); if (dynamic_cast(ptr)) { auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__); if (geodCRS) { return pj_obj_create(ctx, geodCRS->ellipsoid()); } } else { auto datum = dynamic_cast(ptr); if (datum) { return pj_obj_create(ctx, datum->ellipsoid()); } } proj_log_error(ctx, __FUNCTION__, "Object is not a CRS or GeodeticReferenceFrame"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Get the horizontal datum from a CRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type CRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_get_horizontal_datum(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__); if (!geodCRS) { return nullptr; } const auto &datum = geodCRS->datum(); if (datum) { return pj_obj_create(ctx, NN_NO_CHECK(datum)); } const auto &datumEnsemble = geodCRS->datumEnsemble(); if (datumEnsemble) { return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble)); } proj_log_error(ctx, __FUNCTION__, "CRS has no datum"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Return ellipsoid parameters. * * @param ctx PROJ context, or NULL for default context * @param ellipsoid Object of type Ellipsoid (must not be NULL) * @param out_semi_major_metre Pointer to a value to store the semi-major axis * in * metre. or NULL * @param out_semi_minor_metre Pointer to a value to store the semi-minor axis * in * metre. or NULL * @param out_is_semi_minor_computed Pointer to a boolean value to indicate if * the * semi-minor value was computed. If FALSE, its value comes from the * definition. or NULL * @param out_inv_flattening Pointer to a value to store the inverse * flattening. or NULL * @return TRUE in case of success. */ int proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, const PJ *ellipsoid, double *out_semi_major_metre, double *out_semi_minor_metre, int *out_is_semi_minor_computed, double *out_inv_flattening) { SANITIZE_CTX(ctx); assert(ellipsoid); auto l_ellipsoid = dynamic_cast(ellipsoid->iso_obj.get()); if (!l_ellipsoid) { proj_log_error(ctx, __FUNCTION__, "Object is not a Ellipsoid"); return FALSE; } if (out_semi_major_metre) { *out_semi_major_metre = l_ellipsoid->semiMajorAxis().getSIValue(); } if (out_semi_minor_metre) { *out_semi_minor_metre = l_ellipsoid->computeSemiMinorAxis().getSIValue(); } if (out_is_semi_minor_computed) { *out_is_semi_minor_computed = !(l_ellipsoid->semiMinorAxis().has_value()); } if (out_inv_flattening) { *out_inv_flattening = l_ellipsoid->computedInverseFlattening(); } return TRUE; } // --------------------------------------------------------------------------- /** \brief Get the prime meridian of a CRS or a GeodeticReferenceFrame. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_get_prime_meridian(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); auto ptr = obj->iso_obj.get(); if (dynamic_cast(ptr)) { auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__); if (geodCRS) { return pj_obj_create(ctx, geodCRS->primeMeridian()); } } else { auto datum = dynamic_cast(ptr); if (datum) { return pj_obj_create(ctx, datum->primeMeridian()); } } proj_log_error(ctx, __FUNCTION__, "Object is not a CRS or GeodeticReferenceFrame"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Return prime meridian parameters. * * @param ctx PROJ context, or NULL for default context * @param prime_meridian Object of type PrimeMeridian (must not be NULL) * @param out_longitude Pointer to a value to store the longitude of the prime * meridian, in its native unit. or NULL * @param out_unit_conv_factor Pointer to a value to store the conversion * factor of the prime meridian longitude unit to radian. or NULL * @param out_unit_name Pointer to a string value to store the unit name. * or NULL * @return TRUE in case of success. */ int proj_prime_meridian_get_parameters(PJ_CONTEXT *ctx, const PJ *prime_meridian, double *out_longitude, double *out_unit_conv_factor, const char **out_unit_name) { SANITIZE_CTX(ctx); assert(prime_meridian); auto l_pm = dynamic_cast(prime_meridian->iso_obj.get()); if (!l_pm) { proj_log_error(ctx, __FUNCTION__, "Object is not a PrimeMeridian"); return false; } const auto &longitude = l_pm->longitude(); if (out_longitude) { *out_longitude = longitude.value(); } const auto &unit = longitude.unit(); if (out_unit_conv_factor) { *out_unit_conv_factor = unit.conversionToSI(); } if (out_unit_name) { *out_unit_name = unit.name().c_str(); } return true; } // --------------------------------------------------------------------------- /** \brief Return the base CRS of a BoundCRS or a DerivedCRS/ProjectedCRS, or * the source CRS of a CoordinateOperation. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error, or missing source CRS. */ PJ *proj_get_source_crs(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); assert(obj); auto ptr = obj->iso_obj.get(); auto boundCRS = dynamic_cast(ptr); if (boundCRS) { return pj_obj_create(ctx, boundCRS->baseCRS()); } auto derivedCRS = dynamic_cast(ptr); if (derivedCRS) { return pj_obj_create(ctx, derivedCRS->baseCRS()); } auto co = dynamic_cast(ptr); if (co) { auto sourceCRS = co->sourceCRS(); if (sourceCRS) { return pj_obj_create(ctx, NN_NO_CHECK(sourceCRS)); } return nullptr; } if (!obj->alternativeCoordinateOperations.empty()) { return proj_get_source_crs(ctx, obj->alternativeCoordinateOperations[0].pj); } proj_log_error(ctx, __FUNCTION__, "Object is not a BoundCRS or a CoordinateOperation"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Return the hub CRS of a BoundCRS or the target CRS of a * CoordinateOperation. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error, or missing target CRS. */ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); assert(obj); auto ptr = obj->iso_obj.get(); auto boundCRS = dynamic_cast(ptr); if (boundCRS) { return pj_obj_create(ctx, boundCRS->hubCRS()); } auto co = dynamic_cast(ptr); if (co) { auto targetCRS = co->targetCRS(); if (targetCRS) { return pj_obj_create(ctx, NN_NO_CHECK(targetCRS)); } return nullptr; } if (!obj->alternativeCoordinateOperations.empty()) { return proj_get_target_crs(ctx, obj->alternativeCoordinateOperations[0].pj); } proj_log_error(ctx, __FUNCTION__, "Object is not a BoundCRS or a CoordinateOperation"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Identify the CRS with reference CRSs. * * The candidate CRSs are either hard-coded, or looked in the database when * it is available. * * The method returns a list of matching reference CRS, and the percentage * (0-100) of confidence in the match. The list is sorted by decreasing * confidence. *
    *
  • 100% means that the name of the reference entry * perfectly matches the CRS name, and both are equivalent. In which case a * single result is returned. * Note: in the case of a GeographicCRS whose axis * order is implicit in the input definition (for example ESRI WKT), then axis * order is ignored for the purpose of identification. That is the CRS built * from * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]], * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] * will be identified to EPSG:4326, but will not pass a * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test, * but rather isEquivalentTo(EPSG_4326, * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) *
  • *
  • 90% means that CRS are equivalent, but the names are not exactly the * same.
  • *
  • 70% means that CRS are equivalent), but the names do not match at * all.
  • *
  • 25% means that the CRS are not equivalent, but there is some similarity * in * the names.
  • *
* Other confidence values may be returned by some specialized implementations. * * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and * CompoundCRS. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param auth_name Authority name, or NULL for all authorities * @param options Placeholder for future options. Should be set to NULL. * @param out_confidence Output parameter. Pointer to an array of integers that * will be allocated by the function and filled with the confidence values * (0-100). There are as many elements in this array as * proj_list_get_count() * returns on the return value of this function. *confidence should be * released with proj_int_list_destroy(). * @return a list of matching reference CRS, or nullptr in case of error. */ PJ_OBJ_LIST *proj_identify(PJ_CONTEXT *ctx, const PJ *obj, const char *auth_name, const char *const *options, int **out_confidence) { SANITIZE_CTX(ctx); assert(obj); (void)options; if (out_confidence) { *out_confidence = nullptr; } auto ptr = obj->iso_obj.get(); auto crs = dynamic_cast(ptr); if (!crs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CRS"); } else { int *confidenceTemp = nullptr; try { auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name ? auth_name : ""); auto res = crs->identify(factory); std::vector objects; confidenceTemp = out_confidence ? new int[res.size()] : nullptr; size_t i = 0; for (const auto &pair : res) { objects.push_back(pair.first); if (confidenceTemp) { confidenceTemp[i] = pair.second; ++i; } } auto ret = internal::make_unique(std::move(objects)); if (out_confidence) { *out_confidence = confidenceTemp; confidenceTemp = nullptr; } ctx->cpp_context->autoCloseDbIfNeeded(); return ret.release(); } catch (const std::exception &e) { delete[] confidenceTemp; proj_log_error(ctx, __FUNCTION__, e.what()); } } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- /** \brief Free an array of integer. */ void proj_int_list_destroy(int *list) { delete[] list; } // --------------------------------------------------------------------------- /** \brief Return the list of authorities used in the database. * * The returned list is NULL terminated and must be freed with * proj_string_list_destroy(). * * @param ctx PROJ context, or NULL for default context * * @return a NULL terminated list of NUL-terminated strings that must be * freed with proj_string_list_destroy(), or NULL in case of error. */ PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) { SANITIZE_CTX(ctx); try { auto ret = to_string_list(getDBcontext(ctx)->getAuthorities()); ctx->cpp_context->autoCloseDbIfNeeded(); return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- /** \brief Returns the set of authority codes of the given object type. * * The returned list is NULL terminated and must be freed with * proj_string_list_destroy(). * * @param ctx PROJ context, or NULL for default context. * @param auth_name Authority name (must not be NULL) * @param type Object type. * @param allow_deprecated whether we should return deprecated objects as well. * * @return a NULL terminated list of NUL-terminated strings that must be * freed with proj_string_list_destroy(), or NULL in case of error. * * @see proj_get_crs_info_list_from_database() */ PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx, const char *auth_name, PJ_TYPE type, int allow_deprecated) { assert(auth_name); SANITIZE_CTX(ctx); try { auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name); bool valid = false; auto typeInternal = convertPJObjectTypeToObjectType(type, valid); if (!valid) { return nullptr; } auto ret = to_string_list( factory->getAuthorityCodes(typeInternal, allow_deprecated != 0)); ctx->cpp_context->autoCloseDbIfNeeded(); return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- /** Free a list of NULL terminated strings. */ void proj_string_list_destroy(PROJ_STRING_LIST list) { if (list) { for (size_t i = 0; list[i] != nullptr; i++) { delete[] list[i]; } delete[] list; } } // --------------------------------------------------------------------------- /** \brief Instantiate a default set of parameters to be used by * proj_get_crs_list(). * * @return a new object to free with proj_get_crs_list_parameters_destroy() */ PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() { auto ret = new (std::nothrow) PROJ_CRS_LIST_PARAMETERS(); if (ret) { ret->types = nullptr; ret->typesCount = 0; ret->crs_area_of_use_contains_bbox = TRUE; ret->bbox_valid = FALSE; ret->west_lon_degree = 0.0; ret->south_lat_degree = 0.0; ret->east_lon_degree = 0.0; ret->north_lat_degree = 0.0; ret->allow_deprecated = FALSE; } return ret; } // --------------------------------------------------------------------------- /** \brief Destroy an object returned by proj_get_crs_list_parameters_create() */ void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) { delete params; } // --------------------------------------------------------------------------- /** \brief Enumerate CRS objects from the database, taking into account various * criteria. * * The returned object is an array of PROJ_CRS_INFO* pointers, whose last * entry is NULL. This array should be freed with proj_crs_info_list_destroy() * * When no filter parameters are set, this is functionnaly equivalent to * proj_get_crs_info_list_from_database(), instantiating a PJ* object for each * of the proj_create_from_database() and retrieving information with the * various getters. However this function will be much faster. * * @param ctx PROJ context, or NULL for default context * @param auth_name Authority name, used to restrict the search. * Or NULL for all authorities. * @param params Additional criteria, or NULL. If not-NULL, params SHOULD * have been allocated by proj_get_crs_list_parameters_create(), as the * PROJ_CRS_LIST_PARAMETERS structure might grow over time. * @param out_result_count Output parameter pointing to an integer to receive * the size of the result list. Might be NULL * @return an array of PROJ_CRS_INFO* pointers to be freed with * proj_crs_info_list_destroy(), or NULL in case of error. */ PROJ_CRS_INFO ** proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name, const PROJ_CRS_LIST_PARAMETERS *params, int *out_result_count) { SANITIZE_CTX(ctx); PROJ_CRS_INFO **ret = nullptr; int i = 0; try { auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name ? auth_name : ""); auto list = factory->getCRSInfoList(); ret = new PROJ_CRS_INFO *[list.size() + 1]; GeographicBoundingBoxPtr bbox; if (params && params->bbox_valid) { bbox = GeographicBoundingBox::create( params->west_lon_degree, params->south_lat_degree, params->east_lon_degree, params->north_lat_degree) .as_nullable(); } for (const auto &info : list) { auto type = PJ_TYPE_CRS; if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) { type = PJ_TYPE_GEOGRAPHIC_2D_CRS; } else if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS) { type = PJ_TYPE_GEOGRAPHIC_3D_CRS; } else if (info.type == AuthorityFactory::ObjectType::GEOCENTRIC_CRS) { type = PJ_TYPE_GEOCENTRIC_CRS; } else if (info.type == AuthorityFactory::ObjectType::PROJECTED_CRS) { type = PJ_TYPE_PROJECTED_CRS; } else if (info.type == AuthorityFactory::ObjectType::VERTICAL_CRS) { type = PJ_TYPE_VERTICAL_CRS; } else if (info.type == AuthorityFactory::ObjectType::COMPOUND_CRS) { type = PJ_TYPE_COMPOUND_CRS; } if (params && params->typesCount) { bool typeValid = false; for (size_t j = 0; j < params->typesCount; j++) { if (params->types[j] == type) { typeValid = true; break; } else if (params->types[j] == PJ_TYPE_GEOGRAPHIC_CRS && (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) { typeValid = true; break; } else if (params->types[j] == PJ_TYPE_GEODETIC_CRS && (type == PJ_TYPE_GEOCENTRIC_CRS || type == PJ_TYPE_GEOGRAPHIC_2D_CRS || type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) { typeValid = true; break; } } if (!typeValid) { continue; } } if (params && !params->allow_deprecated && info.deprecated) { continue; } if (params && params->bbox_valid) { if (!info.bbox_valid) { continue; } if (info.west_lon_degree <= info.east_lon_degree && params->west_lon_degree <= params->east_lon_degree) { if (params->crs_area_of_use_contains_bbox) { if (params->west_lon_degree < info.west_lon_degree || params->east_lon_degree > info.east_lon_degree || params->south_lat_degree < info.south_lat_degree || params->north_lat_degree > info.north_lat_degree) { continue; } } else { if (info.east_lon_degree < params->west_lon_degree || info.west_lon_degree > params->east_lon_degree || info.north_lat_degree < params->south_lat_degree || info.south_lat_degree > params->north_lat_degree) { continue; } } } else { auto crsExtent = GeographicBoundingBox::create( info.west_lon_degree, info.south_lat_degree, info.east_lon_degree, info.north_lat_degree); if (params->crs_area_of_use_contains_bbox) { if (!crsExtent->contains(NN_NO_CHECK(bbox))) { continue; } } else { if (!bbox->intersects(crsExtent)) { continue; } } } } ret[i] = new PROJ_CRS_INFO; ret[i]->auth_name = pj_strdup(info.authName.c_str()); ret[i]->code = pj_strdup(info.code.c_str()); ret[i]->name = pj_strdup(info.name.c_str()); ret[i]->type = type; ret[i]->deprecated = info.deprecated; ret[i]->bbox_valid = info.bbox_valid; ret[i]->west_lon_degree = info.west_lon_degree; ret[i]->south_lat_degree = info.south_lat_degree; ret[i]->east_lon_degree = info.east_lon_degree; ret[i]->north_lat_degree = info.north_lat_degree; ret[i]->area_name = pj_strdup(info.areaName.c_str()); ret[i]->projection_method_name = info.projectionMethodName.empty() ? nullptr : pj_strdup(info.projectionMethodName.c_str()); i++; } ret[i] = nullptr; if (out_result_count) *out_result_count = i; ctx->cpp_context->autoCloseDbIfNeeded(); return ret; } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ret) { ret[i + 1] = nullptr; proj_crs_info_list_destroy(ret); } if (out_result_count) *out_result_count = 0; } ctx->cpp_context->autoCloseDbIfNeeded(); return nullptr; } // --------------------------------------------------------------------------- /** \brief Destroy the result returned by * proj_get_crs_info_list_from_database(). */ void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) { if (list) { for (int i = 0; list[i] != nullptr; i++) { pj_dalloc(list[i]->auth_name); pj_dalloc(list[i]->code); pj_dalloc(list[i]->name); pj_dalloc(list[i]->area_name); pj_dalloc(list[i]->projection_method_name); delete list[i]; } delete[] list; } } // --------------------------------------------------------------------------- /** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS), * or the Transformation from the baseCRS to the hubCRS of a BoundCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type DerivedCRS or BoundCRSs (must not be NULL) * @return Object of type SingleOperation that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_crs_get_coordoperation(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); assert(crs); SingleOperationPtr co; auto derivedCRS = dynamic_cast(crs->iso_obj.get()); if (derivedCRS) { co = derivedCRS->derivingConversion().as_nullable(); } else { auto boundCRS = dynamic_cast(crs->iso_obj.get()); if (boundCRS) { co = boundCRS->transformation().as_nullable(); } else { proj_log_error(ctx, __FUNCTION__, "Object is not a DerivedCRS or BoundCRS"); return nullptr; } } return pj_obj_create(ctx, NN_NO_CHECK(co)); } // --------------------------------------------------------------------------- /** \brief Return information on the operation method of the SingleOperation. * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type SingleOperation (typically a Conversion * or Transformation) (must not be NULL) * @param out_method_name Pointer to a string value to store the method * (projection) name. or NULL * @param out_method_auth_name Pointer to a string value to store the method * authority name. or NULL * @param out_method_code Pointer to a string value to store the method * code. or NULL * @return TRUE in case of success. */ int proj_coordoperation_get_method_info(PJ_CONTEXT *ctx, const PJ *coordoperation, const char **out_method_name, const char **out_method_auth_name, const char **out_method_code) { SANITIZE_CTX(ctx); assert(coordoperation); auto singleOp = dynamic_cast(coordoperation->iso_obj.get()); if (!singleOp) { proj_log_error(ctx, __FUNCTION__, "Object is not a DerivedCRS or BoundCRS"); return false; } const auto &method = singleOp->method(); const auto &method_ids = method->identifiers(); if (out_method_name) { *out_method_name = method->name()->description()->c_str(); } if (out_method_auth_name) { if (!method_ids.empty()) { *out_method_auth_name = method_ids[0]->codeSpace()->c_str(); } else { *out_method_auth_name = nullptr; } } if (out_method_code) { if (!method_ids.empty()) { *out_method_code = method_ids[0]->code().c_str(); } else { *out_method_code = nullptr; } } return true; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static PropertyMap createPropertyMapName(const char *c_name, const char *auth_name = nullptr, const char *code = nullptr) { std::string name(c_name ? c_name : "unnamed"); PropertyMap properties; if (ends_with(name, " (deprecated)")) { name.resize(name.size() - strlen(" (deprecated)")); properties.set(common::IdentifiedObject::DEPRECATED_KEY, true); } if (auth_name && code) { properties.set(metadata::Identifier::CODESPACE_KEY, auth_name); properties.set(metadata::Identifier::CODE_KEY, code); } return properties.set(common::IdentifiedObject::NAME_KEY, name); } // --------------------------------------------------------------------------- static UnitOfMeasure createLinearUnit(const char *name, double convFactor, const char *unit_auth_name = nullptr, const char *unit_code = nullptr) { return name == nullptr ? UnitOfMeasure::METRE : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::LINEAR, unit_auth_name ? unit_auth_name : "", unit_code ? unit_code : ""); } // --------------------------------------------------------------------------- static UnitOfMeasure createAngularUnit(const char *name, double convFactor, const char *unit_auth_name = nullptr, const char *unit_code = nullptr) { return name ? (ci_equal(name, "degree") ? UnitOfMeasure::DEGREE : ci_equal(name, "grad") ? UnitOfMeasure::GRAD : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::ANGULAR, unit_auth_name ? unit_auth_name : "", unit_code ? unit_code : "")) : UnitOfMeasure::DEGREE; } // --------------------------------------------------------------------------- static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame( PJ_CONTEXT *ctx, const char *datum_name, const char *ellps_name, double semi_major_metre, double inv_flattening, const char *prime_meridian_name, double prime_meridian_offset, const char *angular_units, double angular_units_conv) { const UnitOfMeasure angUnit( createAngularUnit(angular_units, angular_units_conv)); auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); auto body = Ellipsoid::guessBodyName(dbContext, semi_major_metre); auto ellpsName = createPropertyMapName(ellps_name); auto ellps = inv_flattening != 0.0 ? Ellipsoid::createFlattenedSphere( ellpsName, Length(semi_major_metre), Scale(inv_flattening), body) : Ellipsoid::createSphere(ellpsName, Length(semi_major_metre), body); auto pm = PrimeMeridian::create( PropertyMap().set( common::IdentifiedObject::NAME_KEY, prime_meridian_name ? prime_meridian_name : prime_meridian_offset == 0.0 ? (ellps->celestialBody() == Ellipsoid::EARTH ? PrimeMeridian::GREENWICH->nameStr().c_str() : PrimeMeridian::REFERENCE_MERIDIAN->nameStr() .c_str()) : "unnamed"), Angle(prime_meridian_offset, angUnit)); std::string datumName(datum_name ? datum_name : "unnamed"); if (datumName == "WGS_1984") { datumName = GeodeticReferenceFrame::EPSG_6326->nameStr(); } else if (datumName.find('_') != std::string::npos) { // Likely coming from WKT1 if (dbContext) { auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string()); auto res = authFactory->createObjectsFromName( datumName, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true, 1); if (!res.empty()) { const auto &refDatum = res.front(); if (metadata::Identifier::isEquivalentName( datumName.c_str(), refDatum->nameStr().c_str())) { datumName = refDatum->nameStr(); } } else { std::string outTableName; std::string authNameFromAlias; std::string codeFromAlias; auto officialName = authFactory->getOfficialNameFromAlias( datumName, "geodetic_datum", std::string(), true, outTableName, authNameFromAlias, codeFromAlias); if (!officialName.empty()) { datumName = officialName; } } } } return GeodeticReferenceFrame::create( createPropertyMapName(datumName.c_str()), ellps, util::optional(), pm); } //! @endcond // --------------------------------------------------------------------------- /** \brief Create a GeographicCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum_name Name of the GeodeticReferenceFrame. Or NULL * @param ellps_name Name of the Ellipsoid. Or NULL * @param semi_major_metre Ellipsoid semi-major axis, in metres. * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere. * @param prime_meridian_name Name of the PrimeMeridian. Or NULL * @param prime_meridian_offset Offset of the prime meridian, expressed in the * specified angular units. * @param pm_angular_units Name of the angular units. Or NULL for Degree * @param pm_angular_units_conv Conversion factor from the angular unit to * radian. * Or * 0 for Degree if pm_angular_units == NULL. Otherwise should be not NULL * @param ellipsoidal_cs Coordinate system. Must not be NULL. * * @return Object of type GeographicCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, const char *ellps_name, double semi_major_metre, double inv_flattening, const char *prime_meridian_name, double prime_meridian_offset, const char *pm_angular_units, double pm_angular_units_conv, PJ *ellipsoidal_cs) { SANITIZE_CTX(ctx); auto cs = std::dynamic_pointer_cast(ellipsoidal_cs->iso_obj); if (!cs) { return nullptr; } try { auto datum = createGeodeticReferenceFrame( ctx, datum_name, ellps_name, semi_major_metre, inv_flattening, prime_meridian_name, prime_meridian_offset, pm_angular_units, pm_angular_units_conv); auto geogCRS = GeographicCRS::create(createPropertyMapName(crs_name), datum, NN_NO_CHECK(cs)); return pj_obj_create(ctx, geogCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Create a GeographicCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum Datum. Must not be NULL. * @param ellipsoidal_cs Coordinate system. Must not be NULL. * * @return Object of type GeographicCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name, PJ *datum, PJ *ellipsoidal_cs) { SANITIZE_CTX(ctx); auto l_datum = std::dynamic_pointer_cast(datum->iso_obj); if (!l_datum) { proj_log_error(ctx, __FUNCTION__, "datum is not a GeodeticReferenceFrame"); return nullptr; } auto cs = std::dynamic_pointer_cast(ellipsoidal_cs->iso_obj); if (!cs) { return nullptr; } try { auto geogCRS = GeographicCRS::create(createPropertyMapName(crs_name), NN_NO_CHECK(l_datum), NN_NO_CHECK(cs)); return pj_obj_create(ctx, geogCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Create a GeodeticCRS of geocentric type. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum_name Name of the GeodeticReferenceFrame. Or NULL * @param ellps_name Name of the Ellipsoid. Or NULL * @param semi_major_metre Ellipsoid semi-major axis, in metres. * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere. * @param prime_meridian_name Name of the PrimeMeridian. Or NULL * @param prime_meridian_offset Offset of the prime meridian, expressed in the * specified angular units. * @param angular_units Name of the angular units. Or NULL for Degree * @param angular_units_conv Conversion factor from the angular unit to radian. * Or * 0 for Degree if angular_units == NULL. Otherwise should be not NULL * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * * @return Object of type GeodeticCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_geocentric_crs( PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, const char *ellps_name, double semi_major_metre, double inv_flattening, const char *prime_meridian_name, double prime_meridian_offset, const char *angular_units, double angular_units_conv, const char *linear_units, double linear_units_conv) { SANITIZE_CTX(ctx); try { const UnitOfMeasure linearUnit( createLinearUnit(linear_units, linear_units_conv)); auto datum = createGeodeticReferenceFrame( ctx, datum_name, ellps_name, semi_major_metre, inv_flattening, prime_meridian_name, prime_meridian_offset, angular_units, angular_units_conv); auto geodCRS = GeodeticCRS::create(createPropertyMapName(crs_name), datum, cs::CartesianCS::createGeocentric(linearUnit)); return pj_obj_create(ctx, geodCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Create a GeodeticCRS of geocentric type. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum Datum. Must not be NULL. * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * * @return Object of type GeodeticCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_geocentric_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name, const PJ *datum, const char *linear_units, double linear_units_conv) { SANITIZE_CTX(ctx); try { const UnitOfMeasure linearUnit( createLinearUnit(linear_units, linear_units_conv)); auto l_datum = std::dynamic_pointer_cast(datum->iso_obj); if (!l_datum) { proj_log_error(ctx, __FUNCTION__, "datum is not a GeodeticReferenceFrame"); return nullptr; } auto geodCRS = GeodeticCRS::create( createPropertyMapName(crs_name), NN_NO_CHECK(l_datum), cs::CartesianCS::createGeocentric(linearUnit)); return pj_obj_create(ctx, geodCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Create a DerivedGeograhicCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param base_geographic_crs Base Geographic CRS. Must not be NULL. * @param conversion Conversion from the base Geographic to the * DerivedGeograhicCRS. Must not be NULL. * @param ellipsoidal_cs Coordinate system. Must not be NULL. * * @return Object of type GeodeticCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. * * @since 7.0 */ PJ *proj_create_derived_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name, const PJ *base_geographic_crs, const PJ *conversion, const PJ *ellipsoidal_cs) { SANITIZE_CTX(ctx); auto base_crs = std::dynamic_pointer_cast(base_geographic_crs->iso_obj); auto conversion_cpp = std::dynamic_pointer_cast(conversion->iso_obj); auto cs = std::dynamic_pointer_cast(ellipsoidal_cs->iso_obj); if (!base_crs || !conversion_cpp || !cs) { return nullptr; } try { auto derivedCRS = DerivedGeographicCRS::create( createPropertyMapName(crs_name), NN_NO_CHECK(base_crs), NN_NO_CHECK(conversion_cpp), NN_NO_CHECK(cs)); return pj_obj_create(ctx, derivedCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Return whether a CRS is a Derived CRS. * * @param ctx PROJ context, or NULL for default context * @param crs CRS. Must not be NULL. * * @return whether a CRS is a Derived CRS. * * @since 7.0 */ int proj_is_derived_crs(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); return dynamic_cast(crs->iso_obj.get()) != nullptr; } // --------------------------------------------------------------------------- /** \brief Create a VerticalCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum_name Name of the VerticalReferenceFrame. Or NULL * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * * @return Object of type VerticalCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, const char *linear_units, double linear_units_conv) { return proj_create_vertical_crs_ex( ctx, crs_name, datum_name, nullptr, nullptr, linear_units, linear_units_conv, nullptr, nullptr, nullptr, nullptr, nullptr); } // --------------------------------------------------------------------------- /** \brief Create a VerticalCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * This is an extented (_ex) version of proj_create_vertical_crs() that adds * the capability of defining a geoid model. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param datum_name Name of the VerticalReferenceFrame. Or NULL * @param datum_auth_name Authority name of the VerticalReferenceFrame. Or NULL * @param datum_code Code of the VerticalReferenceFrame. Or NULL * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * @param geoid_model_name Geoid model name, or NULL. Can be a name from the * geoid_model name or a string "PROJ foo.gtx" * @param geoid_model_auth_name Authority name of the transformation for * the geoid model. or NULL * @param geoid_model_code Code of the transformation for * the geoid model. or NULL * @param geoid_geog_crs Geographic CRS for the geoid transformation, or NULL. * @param options should be set to NULL for now * @return Object of type VerticalCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_vertical_crs_ex( PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, const char *datum_auth_name, const char *datum_code, const char *linear_units, double linear_units_conv, const char *geoid_model_name, const char *geoid_model_auth_name, const char *geoid_model_code, const PJ *geoid_geog_crs, const char *const *options) { SANITIZE_CTX(ctx); (void)options; try { const UnitOfMeasure linearUnit( createLinearUnit(linear_units, linear_units_conv)); auto datum = VerticalReferenceFrame::create( createPropertyMapName(datum_name, datum_auth_name, datum_code)); auto props = createPropertyMapName(crs_name); auto cs = cs::VerticalCS::createGravityRelatedHeight(linearUnit); if (geoid_model_name) { auto propsModel = createPropertyMapName( geoid_model_name, geoid_model_auth_name, geoid_model_code); const auto vertCRSWithoutGeoid = VerticalCRS::create(props, datum, cs); const auto interpCRS = geoid_geog_crs && std::dynamic_pointer_cast( geoid_geog_crs->iso_obj) ? std::dynamic_pointer_cast(geoid_geog_crs->iso_obj) : nullptr; const auto model(Transformation::create( propsModel, vertCRSWithoutGeoid, GeographicCRS::EPSG_4979, // arbitrarily chosen. Ignored interpCRS, OperationMethod::create(PropertyMap(), std::vector()), {}, {})); props.set("GEOID_MODEL", model); } auto vertCRS = VerticalCRS::create(props, datum, cs); return pj_obj_create(ctx, vertCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Create a CompoundCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name Name of the GeographicCRS. Or NULL * @param horiz_crs Horizontal CRS. must not be NULL. * @param vert_crs Vertical CRS. must not be NULL. * * @return Object of type CompoundCRS that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_compound_crs(PJ_CONTEXT *ctx, const char *crs_name, PJ *horiz_crs, PJ *vert_crs) { assert(horiz_crs); assert(vert_crs); SANITIZE_CTX(ctx); auto l_horiz_crs = std::dynamic_pointer_cast(horiz_crs->iso_obj); if (!l_horiz_crs) { return nullptr; } auto l_vert_crs = std::dynamic_pointer_cast(vert_crs->iso_obj); if (!l_vert_crs) { return nullptr; } try { auto compoundCRS = CompoundCRS::create( createPropertyMapName(crs_name), {NN_NO_CHECK(l_horiz_crs), NN_NO_CHECK(l_vert_crs)}); return pj_obj_create(ctx, compoundCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Return a copy of the object with its name changed * * Currently, only implemented on CRS objects. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param name New name. Must not be NULL * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ PROJ_DLL *proj_alter_name(PJ_CONTEXT *ctx, const PJ *obj, const char *name) { SANITIZE_CTX(ctx); auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { return nullptr; } try { return pj_obj_create(ctx, crs->alterName(name)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Return a copy of the object with its identifier changed/set * * Currently, only implemented on CRS objects. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param auth_name Authority name. Must not be NULL * @param code Code. Must not be NULL * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ PROJ_DLL *proj_alter_id(PJ_CONTEXT *ctx, const PJ *obj, const char *auth_name, const char *code) { SANITIZE_CTX(ctx); auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { return nullptr; } try { return pj_obj_create(ctx, crs->alterId(auth_name, code)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Return a copy of the CRS with its geodetic CRS changed * * Currently, when obj is a GeodeticCRS, it returns a clone of new_geod_crs * When obj is a ProjectedCRS, it replaces its base CRS with new_geod_crs. * When obj is a CompoundCRS, it replaces the GeodeticCRS part of the horizontal * CRS with new_geod_crs. * In other cases, it returns a clone of obj. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param new_geod_crs Object of type GeodeticCRS. Must not be NULL * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_crs_alter_geodetic_crs(PJ_CONTEXT *ctx, const PJ *obj, const PJ *new_geod_crs) { SANITIZE_CTX(ctx); auto l_new_geod_crs = std::dynamic_pointer_cast(new_geod_crs->iso_obj); if (!l_new_geod_crs) { proj_log_error(ctx, __FUNCTION__, "new_geod_crs is not a GeodeticCRS"); return nullptr; } auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { proj_log_error(ctx, __FUNCTION__, "obj is not a CRS"); return nullptr; } try { return pj_obj_create( ctx, crs->alterGeodeticCRS(NN_NO_CHECK(l_new_geod_crs))); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Return a copy of the CRS with its angular units changed * * The CRS must be or contain a GeographicCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param angular_units Name of the angular units. Or NULL for Degree * @param angular_units_conv Conversion factor from the angular unit to radian. * Or 0 for Degree if angular_units == NULL. Otherwise should be not NULL * @param unit_auth_name Unit authority name. Or NULL. * @param unit_code Unit code. Or NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_crs_alter_cs_angular_unit(PJ_CONTEXT *ctx, const PJ *obj, const char *angular_units, double angular_units_conv, const char *unit_auth_name, const char *unit_code) { SANITIZE_CTX(ctx); auto geodCRS = proj_crs_get_geodetic_crs(ctx, obj); if (!geodCRS) { return nullptr; } auto geogCRS = dynamic_cast(geodCRS->iso_obj.get()); if (!geogCRS) { proj_destroy(geodCRS); return nullptr; } PJ *geogCRSAltered = nullptr; try { const UnitOfMeasure angUnit(createAngularUnit( angular_units, angular_units_conv, unit_auth_name, unit_code)); geogCRSAltered = pj_obj_create( ctx, GeographicCRS::create( createPropertyMapName(proj_get_name(geodCRS)), geogCRS->datum(), geogCRS->datumEnsemble(), geogCRS->coordinateSystem()->alterAngularUnit(angUnit))); proj_destroy(geodCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); proj_destroy(geodCRS); return nullptr; } auto ret = proj_crs_alter_geodetic_crs(ctx, obj, geogCRSAltered); proj_destroy(geogCRSAltered); return ret; } // --------------------------------------------------------------------------- /** \brief Return a copy of the CRS with the linear units of its coordinate * system changed * * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS. Must not be NULL * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * @param unit_auth_name Unit authority name. Or NULL. * @param unit_code Unit code. Or NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_crs_alter_cs_linear_unit(PJ_CONTEXT *ctx, const PJ *obj, const char *linear_units, double linear_units_conv, const char *unit_auth_name, const char *unit_code) { SANITIZE_CTX(ctx); auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { return nullptr; } try { const UnitOfMeasure linearUnit(createLinearUnit( linear_units, linear_units_conv, unit_auth_name, unit_code)); return pj_obj_create(ctx, crs->alterCSLinearUnit(linearUnit)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Return a copy of the CRS with the linear units of the parameters * of its conversion modified. * * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type ProjectedCRS. Must not be NULL * @param linear_units Name of the linear units. Or NULL for Metre * @param linear_units_conv Conversion factor from the linear unit to metre. Or * 0 for Metre if linear_units == NULL. Otherwise should be not NULL * @param unit_auth_name Unit authority name. Or NULL. * @param unit_code Unit code. Or NULL. * @param convert_to_new_unit TRUE if existing values should be converted from * their current unit to the new unit. If FALSE, their value will be left * unchanged and the unit overridden (so the resulting CRS will not be * equivalent to the original one for reprojection purposes). * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_crs_alter_parameters_linear_unit(PJ_CONTEXT *ctx, const PJ *obj, const char *linear_units, double linear_units_conv, const char *unit_auth_name, const char *unit_code, int convert_to_new_unit) { SANITIZE_CTX(ctx); auto crs = dynamic_cast(obj->iso_obj.get()); if (!crs) { return nullptr; } try { const UnitOfMeasure linearUnit(createLinearUnit( linear_units, linear_units_conv, unit_auth_name, unit_code)); return pj_obj_create(ctx, crs->alterParametersLinearUnit( linearUnit, convert_to_new_unit == TRUE)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Create a 3D CRS from an existing 2D CRS. * * The new axis will be ellipsoidal height, oriented upwards, and with metre * units. * * See osgeo::proj::crs::CRS::promoteTo3D(). * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_3D_name CRS name. Or NULL (in which case the name of crs_2D * will be used) * @param crs_2D 2D CRS to be "promoted" to 3D. Must not be NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. * @since 6.3 */ PJ *proj_crs_promote_to_3D(PJ_CONTEXT *ctx, const char *crs_3D_name, const PJ *crs_2D) { SANITIZE_CTX(ctx); auto cpp_2D_crs = dynamic_cast(crs_2D->iso_obj.get()); if (!cpp_2D_crs) { proj_log_error(ctx, __FUNCTION__, "crs_2D is not a CRS"); return nullptr; } try { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); return pj_obj_create( ctx, cpp_2D_crs->promoteTo3D(crs_3D_name ? std::string(crs_3D_name) : cpp_2D_crs->nameStr(), dbContext)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Create a projected 3D CRS from an existing projected 2D CRS. * * The passed projected_2D_crs is used so that its name is replaced by * crs_name and its base geographic CRS is replaced by geog_3D_crs. The vertical * axis of geog_3D_crs (ellipsoidal height) will be added as the 3rd axis of * the resulting projected 3D CRS. * Normally, the passed geog_3D_crs should be the 3D counterpart of the original * 2D base geographic CRS of projected_2D_crs, but such no check is done. * * It is also possible to invoke this function with a NULL geog_3D_crs. In which * case, the existing base geographic 2D CRS of projected_2D_crs will be * automatically promoted to 3D by assuming a 3rd axis being an ellipsoidal * height, oriented upwards, and with metre units. This is equivalent to using * proj_crs_promote_to_3D(). * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name CRS name. Or NULL (in which case the name of projected_2D_crs * will be used) * @param projected_2D_crs Projected 2D CRS to be "promoted" to 3D. Must not be * NULL. * @param geog_3D_crs Base geographic 3D CRS for the new CRS. May be NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. * @since 6.3 */ PJ *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx, const char *crs_name, const PJ *projected_2D_crs, const PJ *geog_3D_crs) { SANITIZE_CTX(ctx); auto cpp_projected_2D_crs = dynamic_cast(projected_2D_crs->iso_obj.get()); if (!cpp_projected_2D_crs) { proj_log_error(ctx, __FUNCTION__, "projected_2D_crs is not a Projected CRS"); return nullptr; } const auto &oldCS = cpp_projected_2D_crs->coordinateSystem(); const auto &oldCSAxisList = oldCS->axisList(); if (geog_3D_crs && geog_3D_crs->iso_obj) { auto cpp_geog_3D_CRS = std::dynamic_pointer_cast(geog_3D_crs->iso_obj); if (!cpp_geog_3D_CRS) { proj_log_error(ctx, __FUNCTION__, "geog_3D_crs is not a Geographic CRS"); return nullptr; } const auto &geogCS = cpp_geog_3D_CRS->coordinateSystem(); const auto &geogCSAxisList = geogCS->axisList(); if (geogCSAxisList.size() != 3) { proj_log_error(ctx, __FUNCTION__, "geog_3D_crs is not a Geographic 3D CRS"); return nullptr; } try { auto newCS = cs::CartesianCS::create(PropertyMap(), oldCSAxisList[0], oldCSAxisList[1], geogCSAxisList[2]); return pj_obj_create( ctx, ProjectedCRS::create( createPropertyMapName( crs_name ? crs_name : cpp_projected_2D_crs->nameStr().c_str()), NN_NO_CHECK(cpp_geog_3D_CRS), cpp_projected_2D_crs->derivingConversion(), newCS)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } else { try { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); return pj_obj_create(ctx, cpp_projected_2D_crs->promoteTo3D( crs_name ? std::string(crs_name) : cpp_projected_2D_crs->nameStr(), dbContext)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } } // --------------------------------------------------------------------------- /** \brief Create a 2D CRS from an existing 3D CRS. * * See osgeo::proj::crs::CRS::demoteTo2D(). * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_2D_name CRS name. Or NULL (in which case the name of crs_3D * will be used) * @param crs_3D 3D CRS to be "demoted" to 2D. Must not be NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. * @since 6.3 */ PJ *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, const char *crs_2D_name, const PJ *crs_3D) { SANITIZE_CTX(ctx); auto cpp_3D_crs = dynamic_cast(crs_3D->iso_obj.get()); if (!cpp_3D_crs) { proj_log_error(ctx, __FUNCTION__, "crs_3D is not a CRS"); return nullptr; } try { auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); return pj_obj_create( ctx, cpp_3D_crs->demoteTo2D(crs_2D_name ? std::string(crs_2D_name) : cpp_3D_crs->nameStr(), dbContext)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } } // --------------------------------------------------------------------------- /** \brief Instantiate a EngineeringCRS with just a name * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name CRS name. Or NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ PROJ_DLL *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crs_name) { SANITIZE_CTX(ctx); try { return pj_obj_create( ctx, EngineeringCRS::create( createPropertyMapName(crs_name), EngineeringDatum::create(PropertyMap()), CartesianCS::createEastingNorthing(UnitOfMeasure::METRE))); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static void setSingleOperationElements( const char *name, const char *auth_name, const char *code, const char *method_name, const char *method_auth_name, const char *method_code, int param_count, const PJ_PARAM_DESCRIPTION *params, PropertyMap &propSingleOp, PropertyMap &propMethod, std::vector ¶meters, std::vector &values) { propSingleOp.set(common::IdentifiedObject::NAME_KEY, name ? name : "unnamed"); if (auth_name && code) { propSingleOp.set(metadata::Identifier::CODESPACE_KEY, auth_name) .set(metadata::Identifier::CODE_KEY, code); } propMethod.set(common::IdentifiedObject::NAME_KEY, method_name ? method_name : "unnamed"); if (method_auth_name && method_code) { propMethod.set(metadata::Identifier::CODESPACE_KEY, method_auth_name) .set(metadata::Identifier::CODE_KEY, method_code); } for (int i = 0; i < param_count; i++) { PropertyMap propParam; propParam.set(common::IdentifiedObject::NAME_KEY, params[i].name ? params[i].name : "unnamed"); if (params[i].auth_name && params[i].code) { propParam .set(metadata::Identifier::CODESPACE_KEY, params[i].auth_name) .set(metadata::Identifier::CODE_KEY, params[i].code); } parameters.emplace_back(OperationParameter::create(propParam)); auto unit_type = UnitOfMeasure::Type::UNKNOWN; switch (params[i].unit_type) { case PJ_UT_ANGULAR: unit_type = UnitOfMeasure::Type::ANGULAR; break; case PJ_UT_LINEAR: unit_type = UnitOfMeasure::Type::LINEAR; break; case PJ_UT_SCALE: unit_type = UnitOfMeasure::Type::SCALE; break; case PJ_UT_TIME: unit_type = UnitOfMeasure::Type::TIME; break; case PJ_UT_PARAMETRIC: unit_type = UnitOfMeasure::Type::PARAMETRIC; break; } Measure measure( params[i].value, params[i].unit_type == PJ_UT_ANGULAR ? createAngularUnit(params[i].unit_name, params[i].unit_conv_factor) : params[i].unit_type == PJ_UT_LINEAR ? createLinearUnit(params[i].unit_name, params[i].unit_conv_factor) : UnitOfMeasure(params[i].unit_name ? params[i].unit_name : "unnamed", params[i].unit_conv_factor, unit_type)); values.emplace_back(ParameterValue::create(measure)); } } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Conversion * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param name Conversion name. Or NULL. * @param auth_name Conversion authority name. Or NULL. * @param code Conversion code. Or NULL. * @param method_name Method name. Or NULL. * @param method_auth_name Method authority name. Or NULL. * @param method_code Method code. Or NULL. * @param param_count Number of parameters (size of params argument) * @param params Parameter descriptions (array of size param_count) * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_conversion(PJ_CONTEXT *ctx, const char *name, const char *auth_name, const char *code, const char *method_name, const char *method_auth_name, const char *method_code, int param_count, const PJ_PARAM_DESCRIPTION *params) { SANITIZE_CTX(ctx); try { PropertyMap propSingleOp; PropertyMap propMethod; std::vector parameters; std::vector values; setSingleOperationElements( name, auth_name, code, method_name, method_auth_name, method_code, param_count, params, propSingleOp, propMethod, parameters, values); return pj_obj_create(ctx, Conversion::create(propSingleOp, propMethod, parameters, values)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Instantiate a Transformation * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param name Transformation name. Or NULL. * @param auth_name Transformation authority name. Or NULL. * @param code Transformation code. Or NULL. * @param source_crs Object of type CRS representing the source CRS. * Must not be NULL. * @param target_crs Object of type CRS representing the target CRS. * Must not be NULL. * @param interpolation_crs Object of type CRS representing the interpolation * CRS. Or NULL. * @param method_name Method name. Or NULL. * @param method_auth_name Method authority name. Or NULL. * @param method_code Method code. Or NULL. * @param param_count Number of parameters (size of params argument) * @param params Parameter descriptions (array of size param_count) * @param accuracy Accuracy of the transformation in meters. A negative * values means unknown. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_transformation(PJ_CONTEXT *ctx, const char *name, const char *auth_name, const char *code, PJ *source_crs, PJ *target_crs, PJ *interpolation_crs, const char *method_name, const char *method_auth_name, const char *method_code, int param_count, const PJ_PARAM_DESCRIPTION *params, double accuracy) { SANITIZE_CTX(ctx); assert(source_crs); assert(target_crs); auto l_sourceCRS = std::dynamic_pointer_cast(source_crs->iso_obj); if (!l_sourceCRS) { proj_log_error(ctx, __FUNCTION__, "source_crs is not a CRS"); return nullptr; } auto l_targetCRS = std::dynamic_pointer_cast(target_crs->iso_obj); if (!l_targetCRS) { proj_log_error(ctx, __FUNCTION__, "target_crs is not a CRS"); return nullptr; } CRSPtr l_interpolationCRS; if (interpolation_crs) { l_interpolationCRS = std::dynamic_pointer_cast(interpolation_crs->iso_obj); if (!l_interpolationCRS) { proj_log_error(ctx, __FUNCTION__, "interpolation_crs is not a CRS"); return nullptr; } } try { PropertyMap propSingleOp; PropertyMap propMethod; std::vector parameters; std::vector values; setSingleOperationElements( name, auth_name, code, method_name, method_auth_name, method_code, param_count, params, propSingleOp, propMethod, parameters, values); std::vector accuracies; if (accuracy >= 0.0) { accuracies.emplace_back( PositionalAccuracy::create(toString(accuracy))); } return pj_obj_create( ctx, Transformation::create(propSingleOp, NN_NO_CHECK(l_sourceCRS), NN_NO_CHECK(l_targetCRS), l_interpolationCRS, propMethod, parameters, values, accuracies)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** * \brief Return an equivalent projection. * * Currently implemented: *
    *
  • EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP) to * EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP)
  • *
  • EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP) to * EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP)
  • *
  • EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP to * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP
  • *
  • EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP to * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP
  • *
* * @param ctx PROJ context, or NULL for default context * @param conversion Object of type Conversion. Must not be NULL. * @param new_method_epsg_code EPSG code of the target method. Or 0 (in which * case new_method_name must be specified). * @param new_method_name EPSG or PROJ target method name. Or nullptr (in which * case new_method_epsg_code must be specified). * @return new conversion that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_convert_conversion_to_other_method(PJ_CONTEXT *ctx, const PJ *conversion, int new_method_epsg_code, const char *new_method_name) { SANITIZE_CTX(ctx); auto conv = dynamic_cast(conversion->iso_obj.get()); if (!conv) { proj_log_error(ctx, __FUNCTION__, "not a Conversion"); return nullptr; } if (new_method_epsg_code == 0) { if (!new_method_name) { return nullptr; } if (metadata::Identifier::isEquivalentName( new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_A)) { new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A; } else if (metadata::Identifier::isEquivalentName( new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_B)) { new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B; } else if (metadata::Identifier::isEquivalentName( new_method_name, EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) { new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP; } else if (metadata::Identifier::isEquivalentName( new_method_name, EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP)) { new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP; } } try { auto new_conv = conv->convertToOtherMethod(new_method_epsg_code); if (!new_conv) return nullptr; return pj_obj_create(ctx, NN_NO_CHECK(new_conv)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static CoordinateSystemAxisNNPtr createAxis(const PJ_AXIS_DESCRIPTION &axis) { const auto dir = axis.direction ? AxisDirection::valueOf(axis.direction) : nullptr; if (dir == nullptr) throw Exception("invalid value for axis direction"); auto unit_type = UnitOfMeasure::Type::UNKNOWN; switch (axis.unit_type) { case PJ_UT_ANGULAR: unit_type = UnitOfMeasure::Type::ANGULAR; break; case PJ_UT_LINEAR: unit_type = UnitOfMeasure::Type::LINEAR; break; case PJ_UT_SCALE: unit_type = UnitOfMeasure::Type::SCALE; break; case PJ_UT_TIME: unit_type = UnitOfMeasure::Type::TIME; break; case PJ_UT_PARAMETRIC: unit_type = UnitOfMeasure::Type::PARAMETRIC; break; } auto unit = axis.unit_type == PJ_UT_ANGULAR ? createAngularUnit(axis.unit_name, axis.unit_conv_factor) : axis.unit_type == PJ_UT_LINEAR ? createLinearUnit(axis.unit_name, axis.unit_conv_factor) : UnitOfMeasure(axis.unit_name ? axis.unit_name : "unnamed", axis.unit_conv_factor, unit_type); return CoordinateSystemAxis::create( createPropertyMapName(axis.name), axis.abbreviation ? axis.abbreviation : std::string(), *dir, unit); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a CoordinateSystem. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param type Coordinate system type. * @param axis_count Number of axis * @param axis Axis description (array of size axis_count) * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_cs(PJ_CONTEXT *ctx, PJ_COORDINATE_SYSTEM_TYPE type, int axis_count, const PJ_AXIS_DESCRIPTION *axis) { try { switch (type) { case PJ_CS_TYPE_UNKNOWN: return nullptr; case PJ_CS_TYPE_CARTESIAN: { if (axis_count == 2) { return pj_obj_create( ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]), createAxis(axis[1]))); } else if (axis_count == 3) { return pj_obj_create( ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]), createAxis(axis[1]), createAxis(axis[2]))); } break; } case PJ_CS_TYPE_ELLIPSOIDAL: { if (axis_count == 2) { return pj_obj_create( ctx, EllipsoidalCS::create(PropertyMap(), createAxis(axis[0]), createAxis(axis[1]))); } else if (axis_count == 3) { return pj_obj_create( ctx, EllipsoidalCS::create( PropertyMap(), createAxis(axis[0]), createAxis(axis[1]), createAxis(axis[2]))); } break; } case PJ_CS_TYPE_VERTICAL: { if (axis_count == 1) { return pj_obj_create( ctx, VerticalCS::create(PropertyMap(), createAxis(axis[0]))); } break; } case PJ_CS_TYPE_SPHERICAL: { if (axis_count == 3) { return pj_obj_create( ctx, EllipsoidalCS::create( PropertyMap(), createAxis(axis[0]), createAxis(axis[1]), createAxis(axis[2]))); } break; } case PJ_CS_TYPE_PARAMETRIC: { if (axis_count == 1) { return pj_obj_create( ctx, ParametricCS::create(PropertyMap(), createAxis(axis[0]))); } break; } case PJ_CS_TYPE_ORDINAL: { std::vector axisVector; for (int i = 0; i < axis_count; i++) { axisVector.emplace_back(createAxis(axis[i])); } return pj_obj_create(ctx, OrdinalCS::create(PropertyMap(), axisVector)); } case PJ_CS_TYPE_DATETIMETEMPORAL: { if (axis_count == 1) { return pj_obj_create( ctx, DateTimeTemporalCS::create(PropertyMap(), createAxis(axis[0]))); } break; } case PJ_CS_TYPE_TEMPORALCOUNT: { if (axis_count == 1) { return pj_obj_create( ctx, TemporalCountCS::create(PropertyMap(), createAxis(axis[0]))); } break; } case PJ_CS_TYPE_TEMPORALMEASURE: { if (axis_count == 1) { return pj_obj_create( ctx, TemporalMeasureCS::create(PropertyMap(), createAxis(axis[0]))); } break; } } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } proj_log_error(ctx, __FUNCTION__, "Wrong value for axis_count"); return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesiansCS 2D * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param type Coordinate system type. * @param unit_name Unit name. * @param unit_conv_factor Unit conversion factor to SI. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_cartesian_2D_cs(PJ_CONTEXT *ctx, PJ_CARTESIAN_CS_2D_TYPE type, const char *unit_name, double unit_conv_factor) { try { switch (type) { case PJ_CART2D_EASTING_NORTHING: return pj_obj_create( ctx, CartesianCS::createEastingNorthing( createLinearUnit(unit_name, unit_conv_factor))); case PJ_CART2D_NORTHING_EASTING: return pj_obj_create( ctx, CartesianCS::createNorthingEasting( createLinearUnit(unit_name, unit_conv_factor))); case PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH: return pj_obj_create( ctx, CartesianCS::createNorthPoleEastingSouthNorthingSouth( createLinearUnit(unit_name, unit_conv_factor))); case PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH: return pj_obj_create( ctx, CartesianCS::createSouthPoleEastingNorthNorthingNorth( createLinearUnit(unit_name, unit_conv_factor))); case PJ_CART2D_WESTING_SOUTHING: return pj_obj_create( ctx, CartesianCS::createWestingSouthing( createLinearUnit(unit_name, unit_conv_factor))); } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a Ellipsoidal 2D * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param type Coordinate system type. * @param unit_name Unit name. * @param unit_conv_factor Unit conversion factor to SI. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx, PJ_ELLIPSOIDAL_CS_2D_TYPE type, const char *unit_name, double unit_conv_factor) { try { switch (type) { case PJ_ELLPS2D_LONGITUDE_LATITUDE: return pj_obj_create( ctx, EllipsoidalCS::createLongitudeLatitude( createAngularUnit(unit_name, unit_conv_factor))); case PJ_ELLPS2D_LATITUDE_LONGITUDE: return pj_obj_create( ctx, EllipsoidalCS::createLatitudeLongitude( createAngularUnit(unit_name, unit_conv_factor))); } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a Ellipsoidal 3D * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param type Coordinate system type. * @param horizontal_angular_unit_name Horizontal angular unit name. * @param horizontal_angular_unit_conv_factor Horizontal angular unit conversion * factor to SI. * @param vertical_linear_unit_name Vertical linear unit name. * @param vertical_linear_unit_conv_factor Vertical linear unit conversion * factor to SI. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. * @since 6.3 */ PJ *proj_create_ellipsoidal_3D_cs(PJ_CONTEXT *ctx, PJ_ELLIPSOIDAL_CS_3D_TYPE type, const char *horizontal_angular_unit_name, double horizontal_angular_unit_conv_factor, const char *vertical_linear_unit_name, double vertical_linear_unit_conv_factor) { try { switch (type) { case PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT: return pj_obj_create( ctx, EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight( createAngularUnit(horizontal_angular_unit_name, horizontal_angular_unit_conv_factor), createLinearUnit(vertical_linear_unit_name, vertical_linear_unit_conv_factor))); case PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT: return pj_obj_create( ctx, EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight( createAngularUnit(horizontal_angular_unit_name, horizontal_angular_unit_conv_factor), createLinearUnit(vertical_linear_unit_name, vertical_linear_unit_conv_factor))); } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs_name CRS name. Or NULL * @param geodetic_crs Base GeodeticCRS. Must not be NULL. * @param conversion Conversion. Must not be NULL. * @param coordinate_system Cartesian coordinate system. Must not be NULL. * * @return Object that must be unreferenced with * proj_destroy(), or NULL in case of error. */ PJ *proj_create_projected_crs(PJ_CONTEXT *ctx, const char *crs_name, const PJ *geodetic_crs, const PJ *conversion, const PJ *coordinate_system) { SANITIZE_CTX(ctx); auto geodCRS = std::dynamic_pointer_cast(geodetic_crs->iso_obj); if (!geodCRS) { return nullptr; } auto conv = std::dynamic_pointer_cast(conversion->iso_obj); if (!conv) { return nullptr; } auto cs = std::dynamic_pointer_cast(coordinate_system->iso_obj); if (!cs) { return nullptr; } try { return pj_obj_create( ctx, ProjectedCRS::create(createPropertyMapName(crs_name), NN_NO_CHECK(geodCRS), NN_NO_CHECK(conv), NN_NO_CHECK(cs))); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static PJ *proj_create_conversion(PJ_CONTEXT *ctx, const ConversionNNPtr &conv) { return pj_obj_create(ctx, conv); } //! @endcond /* BEGIN: Generated by scripts/create_c_api_projections.py*/ // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a Universal Transverse Mercator * conversion. * * See osgeo::proj::operation::Conversion::createUTM(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). */ PJ *proj_create_conversion_utm(PJ_CONTEXT *ctx, int zone, int north) { SANITIZE_CTX(ctx); try { auto conv = Conversion::createUTM(PropertyMap(), zone, north != 0); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse * Mercator projection method. * * See osgeo::proj::operation::Conversion::createTransverseMercator(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_transverse_mercator( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createTransverseMercator( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Gauss * Schreiber Transverse Mercator projection method. * * See * osgeo::proj::operation::Conversion::createGaussSchreiberTransverseMercator(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_gauss_schreiber_transverse_mercator( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGaussSchreiberTransverseMercator( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse * Mercator South Orientated projection method. * * See * osgeo::proj::operation::Conversion::createTransverseMercatorSouthOriented(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_transverse_mercator_south_oriented( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createTransverseMercatorSouthOriented( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Two Point * Equidistant projection method. * * See osgeo::proj::operation::Conversion::createTwoPointEquidistant(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_two_point_equidistant( PJ_CONTEXT *ctx, double latitude_first_point, double longitude_first_point, double latitude_second_point, double longitude_secon_point, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createTwoPointEquidistant( PropertyMap(), Angle(latitude_first_point, angUnit), Angle(longitude_first_point, angUnit), Angle(latitude_second_point, angUnit), Angle(longitude_secon_point, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia * Mapping Grid projection method. * * See osgeo::proj::operation::Conversion::createTunisiaMappingGrid(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_tunisia_mapping_grid( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createTunisiaMappingGrid( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Albers * Conic Equal Area projection method. * * See osgeo::proj::operation::Conversion::createAlbersEqualArea(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_albers_equal_area( PJ_CONTEXT *ctx, double latitude_false_origin, double longitude_false_origin, double latitude_first_parallel, double latitude_second_parallel, double easting_false_origin, double northing_false_origin, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createAlbersEqualArea( PropertyMap(), Angle(latitude_false_origin, angUnit), Angle(longitude_false_origin, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(easting_false_origin, linearUnit), Length(northing_false_origin, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Conic Conformal 1SP projection method. * * See osgeo::proj::operation::Conversion::createLambertConicConformal_1SP(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_conic_conformal_1sp( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertConicConformal_1SP( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Conic Conformal (2SP) projection method. * * See osgeo::proj::operation::Conversion::createLambertConicConformal_2SP(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_conic_conformal_2sp( PJ_CONTEXT *ctx, double latitude_false_origin, double longitude_false_origin, double latitude_first_parallel, double latitude_second_parallel, double easting_false_origin, double northing_false_origin, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertConicConformal_2SP( PropertyMap(), Angle(latitude_false_origin, angUnit), Angle(longitude_false_origin, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(easting_false_origin, linearUnit), Length(northing_false_origin, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Conic Conformal (2SP Michigan) projection method. * * See * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Michigan(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_conic_conformal_2sp_michigan( PJ_CONTEXT *ctx, double latitude_false_origin, double longitude_false_origin, double latitude_first_parallel, double latitude_second_parallel, double easting_false_origin, double northing_false_origin, double ellipsoid_scaling_factor, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertConicConformal_2SP_Michigan( PropertyMap(), Angle(latitude_false_origin, angUnit), Angle(longitude_false_origin, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(easting_false_origin, linearUnit), Length(northing_false_origin, linearUnit), Scale(ellipsoid_scaling_factor)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Conic Conformal (2SP Belgium) projection method. * * See * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Belgium(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_conic_conformal_2sp_belgium( PJ_CONTEXT *ctx, double latitude_false_origin, double longitude_false_origin, double latitude_first_parallel, double latitude_second_parallel, double easting_false_origin, double northing_false_origin, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertConicConformal_2SP_Belgium( PropertyMap(), Angle(latitude_false_origin, angUnit), Angle(longitude_false_origin, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(easting_false_origin, linearUnit), Length(northing_false_origin, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Modified * Azimuthal Equidistant projection method. * * See osgeo::proj::operation::Conversion::createAzimuthalEquidistant(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_azimuthal_equidistant( PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createAzimuthalEquidistant( PropertyMap(), Angle(latitude_nat_origin, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Guam * Projection projection method. * * See osgeo::proj::operation::Conversion::createGuamProjection(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_guam_projection( PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGuamProjection( PropertyMap(), Angle(latitude_nat_origin, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Bonne * projection method. * * See osgeo::proj::operation::Conversion::createBonne(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_bonne(PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createBonne( PropertyMap(), Angle(latitude_nat_origin, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Cylindrical Equal Area (Spherical) projection method. * * See * osgeo::proj::operation::Conversion::createLambertCylindricalEqualAreaSpherical(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_cylindrical_equal_area_spherical( PJ_CONTEXT *ctx, double latitude_first_parallel, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertCylindricalEqualAreaSpherical( PropertyMap(), Angle(latitude_first_parallel, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Cylindrical Equal Area (ellipsoidal form) projection method. * * See osgeo::proj::operation::Conversion::createLambertCylindricalEqualArea(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_cylindrical_equal_area( PJ_CONTEXT *ctx, double latitude_first_parallel, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertCylindricalEqualArea( PropertyMap(), Angle(latitude_first_parallel, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Cassini-Soldner projection method. * * See osgeo::proj::operation::Conversion::createCassiniSoldner(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_cassini_soldner( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createCassiniSoldner( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant * Conic projection method. * * See osgeo::proj::operation::Conversion::createEquidistantConic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_equidistant_conic( PJ_CONTEXT *ctx, double center_lat, double center_long, double latitude_first_parallel, double latitude_second_parallel, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEquidistantConic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert I * projection method. * * See osgeo::proj::operation::Conversion::createEckertI(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_i(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertI( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert II * projection method. * * See osgeo::proj::operation::Conversion::createEckertII(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_ii(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertII( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert III * projection method. * * See osgeo::proj::operation::Conversion::createEckertIII(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_iii(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertIII( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert IV * projection method. * * See osgeo::proj::operation::Conversion::createEckertIV(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_iv(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertIV( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert V * projection method. * * See osgeo::proj::operation::Conversion::createEckertV(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_v(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertV( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert VI * projection method. * * See osgeo::proj::operation::Conversion::createEckertVI(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_eckert_vi(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEckertVI( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant * Cylindrical projection method. * * See osgeo::proj::operation::Conversion::createEquidistantCylindrical(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_equidistant_cylindrical( PJ_CONTEXT *ctx, double latitude_first_parallel, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEquidistantCylindrical( PropertyMap(), Angle(latitude_first_parallel, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant * Cylindrical (Spherical) projection method. * * See * osgeo::proj::operation::Conversion::createEquidistantCylindricalSpherical(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_equidistant_cylindrical_spherical( PJ_CONTEXT *ctx, double latitude_first_parallel, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEquidistantCylindricalSpherical( PropertyMap(), Angle(latitude_first_parallel, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Gall * (Stereographic) projection method. * * See osgeo::proj::operation::Conversion::createGall(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_gall(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGall(PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Goode * Homolosine projection method. * * See osgeo::proj::operation::Conversion::createGoodeHomolosine(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_goode_homolosine(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGoodeHomolosine( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Interrupted * Goode Homolosine projection method. * * See osgeo::proj::operation::Conversion::createInterruptedGoodeHomolosine(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_interrupted_goode_homolosine( PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createInterruptedGoodeHomolosine( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Geostationary Satellite View projection method, with the sweep angle axis of * the viewing instrument being x. * * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepX(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_geostationary_satellite_sweep_x( PJ_CONTEXT *ctx, double center_long, double height, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGeostationarySatelliteSweepX( PropertyMap(), Angle(center_long, angUnit), Length(height, linearUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Geostationary Satellite View projection method, with the sweep angle axis of * the viewing instrument being y. * * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepY(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_geostationary_satellite_sweep_y( PJ_CONTEXT *ctx, double center_long, double height, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGeostationarySatelliteSweepY( PropertyMap(), Angle(center_long, angUnit), Length(height, linearUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Gnomonic * projection method. * * See osgeo::proj::operation::Conversion::createGnomonic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_gnomonic(PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createGnomonic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine * Oblique Mercator (Variant A) projection method. * * See * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantA(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_hotine_oblique_mercator_variant_a( PJ_CONTEXT *ctx, double latitude_projection_centre, double longitude_projection_centre, double azimuth_initial_line, double angle_from_rectified_to_skrew_grid, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createHotineObliqueMercatorVariantA( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(longitude_projection_centre, angUnit), Angle(azimuth_initial_line, angUnit), Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine * Oblique Mercator (Variant B) projection method. * * See * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantB(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_hotine_oblique_mercator_variant_b( PJ_CONTEXT *ctx, double latitude_projection_centre, double longitude_projection_centre, double azimuth_initial_line, double angle_from_rectified_to_skrew_grid, double scale, double easting_projection_centre, double northing_projection_centre, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createHotineObliqueMercatorVariantB( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(longitude_projection_centre, angUnit), Angle(azimuth_initial_line, angUnit), Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale), Length(easting_projection_centre, linearUnit), Length(northing_projection_centre, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine * Oblique Mercator Two Point Natural Origin projection method. * * See * osgeo::proj::operation::Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin( PJ_CONTEXT *ctx, double latitude_projection_centre, double latitude_point1, double longitude_point1, double latitude_point2, double longitude_point2, double scale, double easting_projection_centre, double northing_projection_centre, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(latitude_point1, angUnit), Angle(longitude_point1, angUnit), Angle(latitude_point2, angUnit), Angle(longitude_point2, angUnit), Scale(scale), Length(easting_projection_centre, linearUnit), Length(northing_projection_centre, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Laborde * Oblique Mercator projection method. * * See * osgeo::proj::operation::Conversion::createLabordeObliqueMercator(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_laborde_oblique_mercator( PJ_CONTEXT *ctx, double latitude_projection_centre, double longitude_projection_centre, double azimuth_initial_line, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLabordeObliqueMercator( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(longitude_projection_centre, angUnit), Angle(azimuth_initial_line, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * International Map of the World Polyconic projection method. * * See * osgeo::proj::operation::Conversion::createInternationalMapWorldPolyconic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_international_map_world_polyconic( PJ_CONTEXT *ctx, double center_long, double latitude_first_parallel, double latitude_second_parallel, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createInternationalMapWorldPolyconic( PropertyMap(), Angle(center_long, angUnit), Angle(latitude_first_parallel, angUnit), Angle(latitude_second_parallel, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak * (north oriented) projection method. * * See osgeo::proj::operation::Conversion::createKrovakNorthOriented(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_krovak_north_oriented( PJ_CONTEXT *ctx, double latitude_projection_centre, double longitude_of_origin, double colatitude_cone_axis, double latitude_pseudo_standard_parallel, double scale_factor_pseudo_standard_parallel, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createKrovakNorthOriented( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(longitude_of_origin, angUnit), Angle(colatitude_cone_axis, angUnit), Angle(latitude_pseudo_standard_parallel, angUnit), Scale(scale_factor_pseudo_standard_parallel), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak * projection method. * * See osgeo::proj::operation::Conversion::createKrovak(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_krovak( PJ_CONTEXT *ctx, double latitude_projection_centre, double longitude_of_origin, double colatitude_cone_axis, double latitude_pseudo_standard_parallel, double scale_factor_pseudo_standard_parallel, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createKrovak( PropertyMap(), Angle(latitude_projection_centre, angUnit), Angle(longitude_of_origin, angUnit), Angle(colatitude_cone_axis, angUnit), Angle(latitude_pseudo_standard_parallel, angUnit), Scale(scale_factor_pseudo_standard_parallel), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert * Azimuthal Equal Area projection method. * * See osgeo::proj::operation::Conversion::createLambertAzimuthalEqualArea(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_lambert_azimuthal_equal_area( PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createLambertAzimuthalEqualArea( PropertyMap(), Angle(latitude_nat_origin, angUnit), Angle(longitude_nat_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Miller * Cylindrical projection method. * * See osgeo::proj::operation::Conversion::createMillerCylindrical(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_miller_cylindrical( PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createMillerCylindrical( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator * projection method. * * See osgeo::proj::operation::Conversion::createMercatorVariantA(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_mercator_variant_a( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createMercatorVariantA( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator * projection method. * * See osgeo::proj::operation::Conversion::createMercatorVariantB(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_mercator_variant_b( PJ_CONTEXT *ctx, double latitude_first_parallel, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createMercatorVariantB( PropertyMap(), Angle(latitude_first_parallel, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Popular * Visualisation Pseudo Mercator projection method. * * See * osgeo::proj::operation::Conversion::createPopularVisualisationPseudoMercator(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_popular_visualisation_pseudo_mercator( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createPopularVisualisationPseudoMercator( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Mollweide * projection method. * * See osgeo::proj::operation::Conversion::createMollweide(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_mollweide(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createMollweide( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the New Zealand * Map Grid projection method. * * See osgeo::proj::operation::Conversion::createNewZealandMappingGrid(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_new_zealand_mapping_grid( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createNewZealandMappingGrid( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Oblique * Stereographic (Alternative) projection method. * * See osgeo::proj::operation::Conversion::createObliqueStereographic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_oblique_stereographic( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createObliqueStereographic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Orthographic projection method. * * See osgeo::proj::operation::Conversion::createOrthographic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_orthographic( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createOrthographic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the American * Polyconic projection method. * * See osgeo::proj::operation::Conversion::createAmericanPolyconic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_american_polyconic( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createAmericanPolyconic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Polar * Stereographic (Variant A) projection method. * * See osgeo::proj::operation::Conversion::createPolarStereographicVariantA(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_polar_stereographic_variant_a( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createPolarStereographicVariantA( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Polar * Stereographic (Variant B) projection method. * * See osgeo::proj::operation::Conversion::createPolarStereographicVariantB(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_polar_stereographic_variant_b( PJ_CONTEXT *ctx, double latitude_standard_parallel, double longitude_of_origin, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createPolarStereographicVariantB( PropertyMap(), Angle(latitude_standard_parallel, angUnit), Angle(longitude_of_origin, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Robinson * projection method. * * See osgeo::proj::operation::Conversion::createRobinson(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_robinson(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createRobinson( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Sinusoidal * projection method. * * See osgeo::proj::operation::Conversion::createSinusoidal(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_sinusoidal(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createSinusoidal( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Stereographic projection method. * * See osgeo::proj::operation::Conversion::createStereographic(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_stereographic( PJ_CONTEXT *ctx, double center_lat, double center_long, double scale, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createStereographic( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Scale(scale), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Van der * Grinten projection method. * * See osgeo::proj::operation::Conversion::createVanDerGrinten(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_van_der_grinten(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createVanDerGrinten( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner I * projection method. * * See osgeo::proj::operation::Conversion::createWagnerI(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_i(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerI( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner II * projection method. * * See osgeo::proj::operation::Conversion::createWagnerII(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_ii(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerII( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner III * projection method. * * See osgeo::proj::operation::Conversion::createWagnerIII(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_iii( PJ_CONTEXT *ctx, double latitude_true_scale, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerIII( PropertyMap(), Angle(latitude_true_scale, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner IV * projection method. * * See osgeo::proj::operation::Conversion::createWagnerIV(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_iv(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerIV( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner V * projection method. * * See osgeo::proj::operation::Conversion::createWagnerV(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_v(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerV( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VI * projection method. * * See osgeo::proj::operation::Conversion::createWagnerVI(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_vi(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerVI( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VII * projection method. * * See osgeo::proj::operation::Conversion::createWagnerVII(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_wagner_vii(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createWagnerVII( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the * Quadrilateralized Spherical Cube projection method. * * See * osgeo::proj::operation::Conversion::createQuadrilateralizedSphericalCube(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_quadrilateralized_spherical_cube( PJ_CONTEXT *ctx, double center_lat, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createQuadrilateralizedSphericalCube( PropertyMap(), Angle(center_lat, angUnit), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Spherical * Cross-Track Height projection method. * * See osgeo::proj::operation::Conversion::createSphericalCrossTrackHeight(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_spherical_cross_track_height( PJ_CONTEXT *ctx, double peg_point_lat, double peg_point_long, double peg_point_heading, double peg_point_height, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createSphericalCrossTrackHeight( PropertyMap(), Angle(peg_point_lat, angUnit), Angle(peg_point_long, angUnit), Angle(peg_point_heading, angUnit), Length(peg_point_height, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a ProjectedCRS with a conversion based on the Equal Earth * projection method. * * See osgeo::proj::operation::Conversion::createEqualEarth(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createEqualEarth( PropertyMap(), Angle(center_long, angUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Vertical Perspective projection * method. * * See osgeo::proj::operation::Conversion::createVerticalPerspective(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). * * @since 6.3 */ PJ *proj_create_conversion_vertical_perspective( PJ_CONTEXT *ctx, double topo_origin_lat, double topo_origin_long, double topo_origin_height, double view_point_height, double false_easting, double false_northing, const char *ang_unit_name, double ang_unit_conv_factor, const char *linear_unit_name, double linear_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure linearUnit( createLinearUnit(linear_unit_name, linear_unit_conv_factor)); UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createVerticalPerspective( PropertyMap(), Angle(topo_origin_lat, angUnit), Angle(topo_origin_long, angUnit), Length(topo_origin_height, linearUnit), Length(view_point_height, linearUnit), Length(false_easting, linearUnit), Length(false_northing, linearUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Pole Rotation method, using the * conventions of the GRIB 1 and GRIB 2 data formats. * * See osgeo::proj::operation::Conversion::createPoleRotationGRIBConvention(). * * Linear parameters are expressed in (linear_unit_name, * linear_unit_conv_factor). * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor). */ PJ *proj_create_conversion_pole_rotation_grib_convention( PJ_CONTEXT *ctx, double south_pole_lat_in_unrotated_crs, double south_pole_long_in_unrotated_crs, double axis_rotation, const char *ang_unit_name, double ang_unit_conv_factor) { SANITIZE_CTX(ctx); try { UnitOfMeasure angUnit( createAngularUnit(ang_unit_name, ang_unit_conv_factor)); auto conv = Conversion::createPoleRotationGRIBConvention( PropertyMap(), Angle(south_pole_lat_in_unrotated_crs, angUnit), Angle(south_pole_long_in_unrotated_crs, angUnit), Angle(axis_rotation, angUnit)); return proj_create_conversion(ctx, conv); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } return nullptr; } /* END: Generated by scripts/create_c_api_projections.py*/ // --------------------------------------------------------------------------- /** \brief Return whether a coordinate operation can be instantiated as * a PROJ pipeline, checking in particular that referenced grids are * available. * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type CoordinateOperation or derived classes * (must not be NULL) * @return TRUE or FALSE. */ int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx, const PJ *coordoperation) { SANITIZE_CTX(ctx); assert(coordoperation); auto op = dynamic_cast( coordoperation->iso_obj.get()); if (!op) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation"); return 0; } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { auto ret = op->isPROJInstantiable(dbContext) ? 1 : 0; if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return ret; } catch (const std::exception &) { if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return 0; } } // --------------------------------------------------------------------------- /** \brief Return whether a coordinate operation has a "ballpark" * transformation, * that is a very approximate one, due to lack of more accurate transformations. * * Typically a null geographic offset between two horizontal datum, or a * null vertical offset (or limited to unit changes) between two vertical * datum. Errors of several tens to one hundred meters might be expected, * compared to more accurate transformations. * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type CoordinateOperation or derived classes * (must not be NULL) * @return TRUE or FALSE. */ int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx, const PJ *coordoperation) { SANITIZE_CTX(ctx); assert(coordoperation); auto op = dynamic_cast( coordoperation->iso_obj.get()); if (!op) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation"); return 0; } return op->hasBallparkTransformation(); } // --------------------------------------------------------------------------- /** \brief Return the number of parameters of a SingleOperation * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) */ int proj_coordoperation_get_param_count(PJ_CONTEXT *ctx, const PJ *coordoperation) { SANITIZE_CTX(ctx); assert(coordoperation); auto op = dynamic_cast(coordoperation->iso_obj.get()); if (!op) { proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation"); return 0; } return static_cast(op->parameterValues().size()); } // --------------------------------------------------------------------------- /** \brief Return the index of a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param name Parameter name. Must not be NULL * @return index (>=0), or -1 in case of error. */ int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx, const PJ *coordoperation, const char *name) { SANITIZE_CTX(ctx); assert(coordoperation); assert(name); auto op = dynamic_cast(coordoperation->iso_obj.get()); if (!op) { proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation"); return -1; } int index = 0; for (const auto &genParam : op->method()->parameters()) { if (Identifier::isEquivalentName(genParam->nameStr().c_str(), name)) { return index; } index++; } return -1; } // --------------------------------------------------------------------------- /** \brief Return a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param index Parameter index. * @param out_name Pointer to a string value to store the parameter name. or * NULL * @param out_auth_name Pointer to a string value to store the parameter * authority name. or NULL * @param out_code Pointer to a string value to store the parameter * code. or NULL * @param out_value Pointer to a double value to store the parameter * value (if numeric). or NULL * @param out_value_string Pointer to a string value to store the parameter * value (if of type string). or NULL * @param out_unit_conv_factor Pointer to a double value to store the parameter * unit conversion factor. or NULL * @param out_unit_name Pointer to a string value to store the parameter * unit name. or NULL * @param out_unit_auth_name Pointer to a string value to store the * unit authority name. or NULL * @param out_unit_code Pointer to a string value to store the * unit code. or NULL * @param out_unit_category Pointer to a string value to store the parameter * name. or * NULL. This value might be "unknown", "none", "linear", "angular", "scale", * "time" or "parametric"; * @return TRUE in case of success. */ int proj_coordoperation_get_param( PJ_CONTEXT *ctx, const PJ *coordoperation, int index, const char **out_name, const char **out_auth_name, const char **out_code, double *out_value, const char **out_value_string, double *out_unit_conv_factor, const char **out_unit_name, const char **out_unit_auth_name, const char **out_unit_code, const char **out_unit_category) { SANITIZE_CTX(ctx); assert(coordoperation); auto op = dynamic_cast(coordoperation->iso_obj.get()); if (!op) { proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation"); return false; } const auto ¶meters = op->method()->parameters(); const auto &values = op->parameterValues(); if (static_cast(index) >= parameters.size() || static_cast(index) >= values.size()) { proj_log_error(ctx, __FUNCTION__, "Invalid index"); return false; } const auto ¶m = parameters[index]; const auto ¶m_ids = param->identifiers(); if (out_name) { *out_name = param->name()->description()->c_str(); } if (out_auth_name) { if (!param_ids.empty()) { *out_auth_name = param_ids[0]->codeSpace()->c_str(); } else { *out_auth_name = nullptr; } } if (out_code) { if (!param_ids.empty()) { *out_code = param_ids[0]->code().c_str(); } else { *out_code = nullptr; } } const auto &value = values[index]; ParameterValuePtr paramValue = nullptr; auto opParamValue = dynamic_cast(value.get()); if (opParamValue) { paramValue = opParamValue->parameterValue().as_nullable(); } if (out_value) { *out_value = 0; if (paramValue) { if (paramValue->type() == ParameterValue::Type::MEASURE) { *out_value = paramValue->value().value(); } } } if (out_value_string) { *out_value_string = nullptr; if (paramValue) { if (paramValue->type() == ParameterValue::Type::FILENAME) { *out_value_string = paramValue->valueFile().c_str(); } else if (paramValue->type() == ParameterValue::Type::STRING) { *out_value_string = paramValue->stringValue().c_str(); } } } if (out_unit_conv_factor) { *out_unit_conv_factor = 0; } if (out_unit_name) { *out_unit_name = nullptr; } if (out_unit_auth_name) { *out_unit_auth_name = nullptr; } if (out_unit_code) { *out_unit_code = nullptr; } if (out_unit_category) { *out_unit_category = nullptr; } if (paramValue) { if (paramValue->type() == ParameterValue::Type::MEASURE) { const auto &unit = paramValue->value().unit(); if (out_unit_conv_factor) { *out_unit_conv_factor = unit.conversionToSI(); } if (out_unit_name) { *out_unit_name = unit.name().c_str(); } if (out_unit_auth_name) { *out_unit_auth_name = unit.codeSpace().c_str(); } if (out_unit_code) { *out_unit_code = unit.code().c_str(); } if (out_unit_category) { *out_unit_category = get_unit_category(unit.type()); } } } return true; } // --------------------------------------------------------------------------- /** \brief Return the parameters of a Helmert transformation as WKT1 TOWGS84 * values. * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type Transformation, that can be represented * as a WKT1 TOWGS84 node (must not be NULL) * @param out_values Pointer to an array of value_count double values. * @param value_count Size of out_values array. The suggested size is 7 to get * translation, rotation and scale difference parameters. Rotation and scale * difference terms might be zero if the transformation only includes * translation * parameters. In that case, value_count could be set to 3. * @param emit_error_if_incompatible Boolean to inicate if an error must be * logged if coordoperation is not compatible with a WKT1 TOWGS84 * representation. * @return TRUE in case of success, or FALSE if coordoperation is not * compatible with a WKT1 TOWGS84 representation. */ int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx, const PJ *coordoperation, double *out_values, int value_count, int emit_error_if_incompatible) { SANITIZE_CTX(ctx); assert(coordoperation); auto transf = dynamic_cast(coordoperation->iso_obj.get()); if (!transf) { if (emit_error_if_incompatible) { proj_log_error(ctx, __FUNCTION__, "Object is not a Transformation"); } return FALSE; } try { auto values = transf->getTOWGS84Parameters(); for (int i = 0; i < value_count && static_cast(i) < values.size(); i++) { out_values[i] = values[i]; } return TRUE; } catch (const std::exception &e) { if (emit_error_if_incompatible) { proj_log_error(ctx, __FUNCTION__, e.what()); } return FALSE; } } // --------------------------------------------------------------------------- /** \brief Return the number of grids used by a CoordinateOperation * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type CoordinateOperation or derived classes * (must not be NULL) */ int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx, const PJ *coordoperation) { SANITIZE_CTX(ctx); assert(coordoperation); auto co = dynamic_cast( coordoperation->iso_obj.get()); if (!co) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation"); return 0; } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { if (!coordoperation->gridsNeededAsked) { coordoperation->gridsNeededAsked = true; const auto gridsNeeded = co->gridsNeeded(dbContext); for (const auto &gridDesc : gridsNeeded) { coordoperation->gridsNeeded.emplace_back(gridDesc); } } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return static_cast(coordoperation->gridsNeeded.size()); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return 0; } } // --------------------------------------------------------------------------- /** \brief Return a parameter of a SingleOperation * * @param ctx PROJ context, or NULL for default context * @param coordoperation Object of type SingleOperation or derived classes * (must not be NULL) * @param index Parameter index. * @param out_short_name Pointer to a string value to store the grid short name. * or NULL * @param out_full_name Pointer to a string value to store the grid full * filename. or NULL * @param out_package_name Pointer to a string value to store the package name * where * the grid might be found. or NULL * @param out_url Pointer to a string value to store the grid URL or the * package URL where the grid might be found. or NULL * @param out_direct_download Pointer to a int (boolean) value to store whether * *out_url can be downloaded directly. or NULL * @param out_open_license Pointer to a int (boolean) value to store whether * the grid is released with an open license. or NULL * @param out_available Pointer to a int (boolean) value to store whether the * grid is available at runtime. or NULL * @return TRUE in case of success. */ int proj_coordoperation_get_grid_used( PJ_CONTEXT *ctx, const PJ *coordoperation, int index, const char **out_short_name, const char **out_full_name, const char **out_package_name, const char **out_url, int *out_direct_download, int *out_open_license, int *out_available) { SANITIZE_CTX(ctx); const int count = proj_coordoperation_get_grid_used_count(ctx, coordoperation); if (index < 0 || index >= count) { proj_log_error(ctx, __FUNCTION__, "Invalid index"); return false; } const auto &gridDesc = coordoperation->gridsNeeded[index]; if (out_short_name) { *out_short_name = gridDesc.shortName.c_str(); } if (out_full_name) { *out_full_name = gridDesc.fullName.c_str(); } if (out_package_name) { *out_package_name = gridDesc.packageName.c_str(); } if (out_url) { *out_url = gridDesc.url.c_str(); } if (out_direct_download) { *out_direct_download = gridDesc.directDownload; } if (out_open_license) { *out_open_license = gridDesc.openLicense; } if (out_available) { *out_available = gridDesc.available; } return true; } // --------------------------------------------------------------------------- /** \brief Opaque object representing an operation factory context. */ struct PJ_OPERATION_FACTORY_CONTEXT { //! @cond Doxygen_Suppress CoordinateOperationContextNNPtr operationContext; explicit PJ_OPERATION_FACTORY_CONTEXT( CoordinateOperationContextNNPtr &&operationContextIn) : operationContext(std::move(operationContextIn)) {} PJ_OPERATION_FACTORY_CONTEXT(const PJ_OPERATION_FACTORY_CONTEXT &) = delete; PJ_OPERATION_FACTORY_CONTEXT & operator=(const PJ_OPERATION_FACTORY_CONTEXT &) = delete; //! @endcond }; // --------------------------------------------------------------------------- /** \brief Instantiate a context for building coordinate operations between * two CRS. * * The returned object must be unreferenced with * proj_operation_factory_context_destroy() after use. * * If authority is NULL or the empty string, then coordinate * operations from any authority will be searched, with the restrictions set * in the authority_to_authority_preference database table. * If authority is set to "any", then coordinate * operations from any authority will be searched * If authority is a non-empty string different of "any", * then coordinate operations will be searched only in that authority namespace. * * @param ctx Context, or NULL for default context. * @param authority Name of authority to which to restrict the search of * candidate operations. * @return Object that must be unreferenced with * proj_operation_factory_context_destroy(), or NULL in * case of error. */ PJ_OPERATION_FACTORY_CONTEXT * proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) { SANITIZE_CTX(ctx); auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { if (dbContext) { auto factory = CoordinateOperationFactory::create(); auto authFactory = AuthorityFactory::create( NN_NO_CHECK(dbContext), std::string(authority ? authority : "")); auto operationContext = CoordinateOperationContext::create(authFactory, nullptr, 0.0); ctx->cpp_context->autoCloseDbIfNeeded(); return new PJ_OPERATION_FACTORY_CONTEXT( std::move(operationContext)); } else { auto operationContext = CoordinateOperationContext::create(nullptr, nullptr, 0.0); return new PJ_OPERATION_FACTORY_CONTEXT( std::move(operationContext)); } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } if (ctx->cpp_context) { ctx->cpp_context->autoCloseDbIfNeeded(); } return nullptr; } // --------------------------------------------------------------------------- /** \brief Drops a reference on an object. * * This method should be called one and exactly one for each function * returning a PJ_OPERATION_FACTORY_CONTEXT* * * @param ctx Object, or NULL. */ void proj_operation_factory_context_destroy(PJ_OPERATION_FACTORY_CONTEXT *ctx) { delete ctx; } // --------------------------------------------------------------------------- /** \brief Set the desired accuracy of the resulting coordinate transformations. * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param accuracy Accuracy in meter (or 0 to disable the filter). */ void proj_operation_factory_context_set_desired_accuracy( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, double accuracy) { SANITIZE_CTX(ctx); assert(factory_ctx); try { factory_ctx->operationContext->setDesiredAccuracy(accuracy); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set the desired area of interest for the resulting coordinate * transformations. * * For an area of interest crossing the anti-meridian, west_lon_degree will be * greater than east_lon_degree. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param west_lon_degree West longitude (in degrees). * @param south_lat_degree South latitude (in degrees). * @param east_lon_degree East longitude (in degrees). * @param north_lat_degree North latitude (in degrees). */ void proj_operation_factory_context_set_area_of_interest( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, double west_lon_degree, double south_lat_degree, double east_lon_degree, double north_lat_degree) { SANITIZE_CTX(ctx); assert(factory_ctx); try { factory_ctx->operationContext->setAreaOfInterest( Extent::createFromBBOX(west_lon_degree, south_lat_degree, east_lon_degree, north_lat_degree)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set how source and target CRS extent should be used * when considering if a transformation can be used (only takes effect if * no area of interest is explicitly defined). * * The default is PJ_CRS_EXTENT_SMALLEST. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param use How source and target CRS extent should be used. */ void proj_operation_factory_context_set_crs_extent_use( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, PROJ_CRS_EXTENT_USE use) { SANITIZE_CTX(ctx); assert(factory_ctx); try { switch (use) { case PJ_CRS_EXTENT_NONE: factory_ctx->operationContext->setSourceAndTargetCRSExtentUse( CoordinateOperationContext::SourceTargetCRSExtentUse::NONE); break; case PJ_CRS_EXTENT_BOTH: factory_ctx->operationContext->setSourceAndTargetCRSExtentUse( CoordinateOperationContext::SourceTargetCRSExtentUse::BOTH); break; case PJ_CRS_EXTENT_INTERSECTION: factory_ctx->operationContext->setSourceAndTargetCRSExtentUse( CoordinateOperationContext::SourceTargetCRSExtentUse:: INTERSECTION); break; case PJ_CRS_EXTENT_SMALLEST: factory_ctx->operationContext->setSourceAndTargetCRSExtentUse( CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST); break; } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set the spatial criterion to use when comparing the area of * validity of coordinate operations with the area of interest / area of * validity of * source and target CRS. * * The default is PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param criterion patial criterion to use */ void PROJ_DLL proj_operation_factory_context_set_spatial_criterion( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, PROJ_SPATIAL_CRITERION criterion) { SANITIZE_CTX(ctx); assert(factory_ctx); try { switch (criterion) { case PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT: factory_ctx->operationContext->setSpatialCriterion( CoordinateOperationContext::SpatialCriterion:: STRICT_CONTAINMENT); break; case PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION: factory_ctx->operationContext->setSpatialCriterion( CoordinateOperationContext::SpatialCriterion:: PARTIAL_INTERSECTION); break; } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set how grid availability is used. * * The default is USE_FOR_SORTING. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param use how grid availability is used. */ void PROJ_DLL proj_operation_factory_context_set_grid_availability_use( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, PROJ_GRID_AVAILABILITY_USE use) { SANITIZE_CTX(ctx); assert(factory_ctx); try { switch (use) { case PROJ_GRID_AVAILABILITY_USED_FOR_SORTING: factory_ctx->operationContext->setGridAvailabilityUse( CoordinateOperationContext::GridAvailabilityUse:: USE_FOR_SORTING); break; case PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID: factory_ctx->operationContext->setGridAvailabilityUse( CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID); break; case PROJ_GRID_AVAILABILITY_IGNORED: factory_ctx->operationContext->setGridAvailabilityUse( CoordinateOperationContext::GridAvailabilityUse:: IGNORE_GRID_AVAILABILITY); break; } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set whether PROJ alternative grid names should be substituted to * the official authority names. * * The default is true. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param usePROJNames whether PROJ alternative grid names should be used */ void proj_operation_factory_context_set_use_proj_alternative_grid_names( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int usePROJNames) { SANITIZE_CTX(ctx); assert(factory_ctx); try { factory_ctx->operationContext->setUsePROJAlternativeGridNames( usePROJNames != 0); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set whether an intermediate pivot CRS can be used for researching * coordinate operations between a source and target CRS. * * Concretely if in the database there is an operation from A to C * (or C to A), and another one from C to B (or B to C), but no direct * operation between A and B, setting this parameter to true, allow * chaining both operations. * * The current implementation is limited to researching one intermediate * step. * * By default, with the IF_NO_DIRECT_TRANSFORMATION stratgey, all potential * C candidates will be used if there is no direct tranformation. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param use whether and how intermediate CRS may be used. */ void proj_operation_factory_context_set_allow_use_intermediate_crs( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, PROJ_INTERMEDIATE_CRS_USE use) { SANITIZE_CTX(ctx); assert(factory_ctx); try { switch (use) { case PROJ_INTERMEDIATE_CRS_USE_ALWAYS: factory_ctx->operationContext->setAllowUseIntermediateCRS( CoordinateOperationContext::IntermediateCRSUse::ALWAYS); break; case PROJ_INTERMEDIATE_CRS_USE_IF_NO_DIRECT_TRANSFORMATION: factory_ctx->operationContext->setAllowUseIntermediateCRS( CoordinateOperationContext::IntermediateCRSUse:: IF_NO_DIRECT_TRANSFORMATION); break; case PROJ_INTERMEDIATE_CRS_USE_NEVER: factory_ctx->operationContext->setAllowUseIntermediateCRS( CoordinateOperationContext::IntermediateCRSUse::NEVER); break; } } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Restrict the potential pivot CRSs that can be used when trying to * build a coordinate operation between two CRS that have no direct operation. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param list_of_auth_name_codes an array of strings NLL terminated, * with the format { "auth_name1", "code1", "auth_name2", "code2", ... NULL } */ void proj_operation_factory_context_set_allowed_intermediate_crs( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, const char *const *list_of_auth_name_codes) { SANITIZE_CTX(ctx); assert(factory_ctx); try { std::vector> pivots; for (auto iter = list_of_auth_name_codes; iter && iter[0] && iter[1]; iter += 2) { pivots.emplace_back(std::pair( std::string(iter[0]), std::string(iter[1]))); } factory_ctx->operationContext->setIntermediateCRS(pivots); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Set whether transformations that are superseded (but not deprecated) * should be discarded. * * @param ctx PROJ context, or NULL for default context * @param factory_ctx Operation factory context. must not be NULL * @param discard superseded crs or not */ void PROJ_DLL proj_operation_factory_context_set_discard_superseded( PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int discard) { SANITIZE_CTX(ctx); assert(factory_ctx); try { factory_ctx->operationContext->setDiscardSuperseded(discard != 0); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); } } // --------------------------------------------------------------------------- /** \brief Find a list of CoordinateOperation from source_crs to target_crs. * * The operations are sorted with the most relevant ones first: by * descending * area (intersection of the transformation area with the area of interest, * or intersection of the transformation with the area of use of the CRS), * and * by increasing accuracy. Operations with unknown accuracy are sorted last, * whatever their area. * * When one of the source or target CRS has a vertical component but not the * other one, the one that has no vertical component is automatically promoted * to a 3D version, where its vertical axis is the ellipsoidal height in metres, * using the ellipsoid of the base geodetic CRS. * * @param ctx PROJ context, or NULL for default context * @param source_crs source CRS. Must not be NULL. * @param target_crs source CRS. Must not be NULL. * @param operationContext Search context. Must not be NULL. * @return a result set that must be unreferenced with * proj_list_destroy(), or NULL in case of error. */ PJ_OBJ_LIST * proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs, const PJ *target_crs, const PJ_OPERATION_FACTORY_CONTEXT *operationContext) { SANITIZE_CTX(ctx); assert(source_crs); assert(target_crs); assert(operationContext); auto sourceCRS = std::dynamic_pointer_cast(source_crs->iso_obj); if (!sourceCRS) { proj_log_error(ctx, __FUNCTION__, "source_crs is not a CRS"); return nullptr; } auto targetCRS = std::dynamic_pointer_cast(target_crs->iso_obj); if (!targetCRS) { proj_log_error(ctx, __FUNCTION__, "target_crs is not a CRS"); return nullptr; } try { auto factory = CoordinateOperationFactory::create(); std::vector objects; auto ops = factory->createOperations( NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), operationContext->operationContext); for (const auto &op : ops) { objects.emplace_back(op); } return new PJ_OBJ_LIST(std::move(objects)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Return the number of objects in the result set * * @param result Object of type PJ_OBJ_LIST (must not be NULL) */ int proj_list_get_count(const PJ_OBJ_LIST *result) { assert(result); return static_cast(result->objects.size()); } // --------------------------------------------------------------------------- /** \brief Return an object from the result set * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param result Object of type PJ_OBJ_LIST (must not be NULL) * @param index Index * @return a new object that must be unreferenced with proj_destroy(), * or nullptr in case of error. */ PJ *proj_list_get(PJ_CONTEXT *ctx, const PJ_OBJ_LIST *result, int index) { SANITIZE_CTX(ctx); assert(result); if (index < 0 || index >= proj_list_get_count(result)) { proj_log_error(ctx, __FUNCTION__, "Invalid index"); return nullptr; } return pj_obj_create(ctx, result->objects[index]); } // --------------------------------------------------------------------------- /** \brief Drops a reference on the result set. * * This method should be called one and exactly one for each function * returning a PJ_OBJ_LIST* * * @param result Object, or NULL. */ void proj_list_destroy(PJ_OBJ_LIST *result) { delete result; } // --------------------------------------------------------------------------- /** \brief Return the accuracy (in metre) of a coordinate operation. * * @param ctx PROJ context, or NULL for default context * @param coordoperation Coordinate operation. Must not be NULL. * @return the accuracy, or a negative value if unknown or in case of error. */ double proj_coordoperation_get_accuracy(PJ_CONTEXT *ctx, const PJ *coordoperation) { SANITIZE_CTX(ctx); assert(coordoperation); auto co = dynamic_cast( coordoperation->iso_obj.get()); if (!co) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation"); return -1; } const auto &accuracies = co->coordinateOperationAccuracies(); if (accuracies.empty()) { return -1; } try { return c_locale_stod(accuracies[0]->value()); } catch (const std::exception &) { } return -1; } // --------------------------------------------------------------------------- /** \brief Returns the datum of a SingleCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type SingleCRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error (or if there is no datum) */ PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); assert(crs); auto l_crs = dynamic_cast(crs->iso_obj.get()); if (!l_crs) { proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS"); return nullptr; } const auto &datum = l_crs->datum(); if (!datum) { return nullptr; } return pj_obj_create(ctx, NN_NO_CHECK(datum)); } // --------------------------------------------------------------------------- /** \brief Returns the coordinate system of a SingleCRS. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param crs Object of type SingleCRS (must not be NULL) * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs) { SANITIZE_CTX(ctx); assert(crs); auto l_crs = dynamic_cast(crs->iso_obj.get()); if (!l_crs) { proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS"); return nullptr; } return pj_obj_create(ctx, l_crs->coordinateSystem()); } // --------------------------------------------------------------------------- /** \brief Returns the type of the coordinate system. * * @param ctx PROJ context, or NULL for default context * @param cs Object of type CoordinateSystem (must not be NULL) * @return type, or PJ_CS_TYPE_UNKNOWN in case of error. */ PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) { SANITIZE_CTX(ctx); assert(cs); auto l_cs = dynamic_cast(cs->iso_obj.get()); if (!l_cs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem"); return PJ_CS_TYPE_UNKNOWN; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_CARTESIAN; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_ELLIPSOIDAL; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_VERTICAL; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_SPHERICAL; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_ORDINAL; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_PARAMETRIC; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_DATETIMETEMPORAL; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_TEMPORALCOUNT; } if (dynamic_cast(l_cs)) { return PJ_CS_TYPE_TEMPORALMEASURE; } return PJ_CS_TYPE_UNKNOWN; } // --------------------------------------------------------------------------- /** \brief Returns the number of axis of the coordinate system. * * @param ctx PROJ context, or NULL for default context * @param cs Object of type CoordinateSystem (must not be NULL) * @return number of axis, or -1 in case of error. */ int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) { SANITIZE_CTX(ctx); assert(cs); auto l_cs = dynamic_cast(cs->iso_obj.get()); if (!l_cs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem"); return -1; } return static_cast(l_cs->axisList().size()); } // --------------------------------------------------------------------------- /** \brief Returns information on an axis * * @param ctx PROJ context, or NULL for default context * @param cs Object of type CoordinateSystem (must not be NULL) * @param index Index of the coordinate system (between 0 and * proj_cs_get_axis_count() - 1) * @param out_name Pointer to a string value to store the axis name. or NULL * @param out_abbrev Pointer to a string value to store the axis abbreviation. * or NULL * @param out_direction Pointer to a string value to store the axis direction. * or NULL * @param out_unit_conv_factor Pointer to a double value to store the axis * unit conversion factor. or NULL * @param out_unit_name Pointer to a string value to store the axis * unit name. or NULL * @param out_unit_auth_name Pointer to a string value to store the axis * unit authority name. or NULL * @param out_unit_code Pointer to a string value to store the axis * unit code. or NULL * @return TRUE in case of success */ int proj_cs_get_axis_info(PJ_CONTEXT *ctx, const PJ *cs, int index, const char **out_name, const char **out_abbrev, const char **out_direction, double *out_unit_conv_factor, const char **out_unit_name, const char **out_unit_auth_name, const char **out_unit_code) { SANITIZE_CTX(ctx); assert(cs); auto l_cs = dynamic_cast(cs->iso_obj.get()); if (!l_cs) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem"); return false; } const auto &axisList = l_cs->axisList(); if (index < 0 || static_cast(index) >= axisList.size()) { proj_log_error(ctx, __FUNCTION__, "Invalid index"); return false; } const auto &axis = axisList[index]; if (out_name) { *out_name = axis->nameStr().c_str(); } if (out_abbrev) { *out_abbrev = axis->abbreviation().c_str(); } if (out_direction) { *out_direction = axis->direction().toString().c_str(); } if (out_unit_conv_factor) { *out_unit_conv_factor = axis->unit().conversionToSI(); } if (out_unit_name) { *out_unit_name = axis->unit().name().c_str(); } if (out_unit_auth_name) { *out_unit_auth_name = axis->unit().codeSpace().c_str(); } if (out_unit_code) { *out_unit_code = axis->unit().code().c_str(); } return true; } // --------------------------------------------------------------------------- /** \brief Returns a PJ* object whose axis order is the one expected for * visualization purposes. * * The input object must be either: *
    *
  • a coordinate operation, that has been created with * proj_create_crs_to_crs(). If the axis order of its source or target CRS * is northing,easting, then an axis swap operation will be inserted.
  • *
  • or a CRS. The axis order of geographic CRS will be longitude, latitude * [,height], and the one of projected CRS will be easting, northing * [, height]
  • *
* * @param ctx PROJ context, or NULL for default context * @param obj Object of type CRS, or CoordinateOperation created with * proj_create_crs_to_crs() (must not be NULL) * @return a new PJ* object to free with proj_destroy() in case of success, or * nullptr in case of error */ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); if (!obj->alternativeCoordinateOperations.empty()) { try { auto pjNew = std::unique_ptr(pj_new()); if (!pjNew) return nullptr; pjNew->ctx = ctx; for (const auto &alt : obj->alternativeCoordinateOperations) { auto co = dynamic_cast( alt.pj->iso_obj.get()); if (co) { double minxSrc = alt.minxSrc; double minySrc = alt.minySrc; double maxxSrc = alt.maxxSrc; double maxySrc = alt.maxySrc; double minxDst = alt.minxDst; double minyDst = alt.minyDst; double maxxDst = alt.maxxDst; double maxyDst = alt.maxyDst; auto l_sourceCRS = co->sourceCRS(); auto l_targetCRS = co->targetCRS(); if (l_sourceCRS && l_targetCRS) { const bool swapSource = l_sourceCRS ->mustAxisOrderBeSwitchedForVisualization(); if (swapSource) { std::swap(minxSrc, minySrc); std::swap(maxxSrc, maxySrc); } const bool swapTarget = l_targetCRS ->mustAxisOrderBeSwitchedForVisualization(); if (swapTarget) { std::swap(minxDst, minyDst); std::swap(maxxDst, maxyDst); } } pjNew->alternativeCoordinateOperations.emplace_back( minxSrc, minySrc, maxxSrc, maxySrc, minxDst, minyDst, maxxDst, maxyDst, pj_obj_create(ctx, co->normalizeForVisualization()), co->nameStr(), alt.accuracy, alt.isOffshore); } } return pjNew.release(); } catch (const std::exception &e) { proj_log_debug(ctx, __FUNCTION__, e.what()); return nullptr; } } auto crs = dynamic_cast(obj->iso_obj.get()); if (crs) { try { return pj_obj_create(ctx, crs->normalizeForVisualization()); } catch (const std::exception &e) { proj_log_debug(ctx, __FUNCTION__, e.what()); return nullptr; } } auto co = dynamic_cast(obj->iso_obj.get()); if (!co) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation " "created with " "proj_create_crs_to_crs"); return nullptr; } try { return pj_obj_create(ctx, co->normalizeForVisualization()); } catch (const std::exception &e) { proj_log_debug(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Returns a PJ* coordinate operation object which represents the * inverse operation of the specified coordinate operation. * * @param ctx PROJ context, or NULL for default context * @param obj Object of type CoordinateOperation (must not be NULL) * @return a new PJ* object to free with proj_destroy() in case of success, or * nullptr in case of error * @since 6.3 */ PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) { SANITIZE_CTX(ctx); auto co = dynamic_cast(obj->iso_obj.get()); if (!co) { proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateOperation"); return nullptr; } try { return pj_obj_create(ctx, co->inverse()); } catch (const std::exception &e) { proj_log_debug(ctx, __FUNCTION__, e.what()); return nullptr; } } // --------------------------------------------------------------------------- /** \brief Returns the number of steps of a concatenated operation. * * The input object must be a concatenated operation. * * @param ctx PROJ context, or NULL for default context * @param concatoperation Concatenated operation (must not be NULL) * @return the number of steps, or 0 in case of error. */ int proj_concatoperation_get_step_count(PJ_CONTEXT *ctx, const PJ *concatoperation) { SANITIZE_CTX(ctx); assert(concatoperation); auto l_co = dynamic_cast( concatoperation->iso_obj.get()); if (!l_co) { proj_log_error(ctx, __FUNCTION__, "Object is not a ConcatenatedOperation"); return false; } return static_cast(l_co->operations().size()); } // --------------------------------------------------------------------------- /** \brief Returns a step of a concatenated operation. * * The input object must be a concatenated operation. * * The returned object must be unreferenced with proj_destroy() after * use. * It should be used by at most one thread at a time. * * @param ctx PROJ context, or NULL for default context * @param concatoperation Concatenated operation (must not be NULL) * @param i_step Index of the step to extract. Between 0 and * proj_concatoperation_get_step_count()-1 * @return Object that must be unreferenced with proj_destroy(), or NULL * in case of error. */ PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation, int i_step) { SANITIZE_CTX(ctx); assert(concatoperation); auto l_co = dynamic_cast( concatoperation->iso_obj.get()); if (!l_co) { proj_log_error(ctx, __FUNCTION__, "Object is not a ConcatenatedOperation"); return nullptr; } const auto &steps = l_co->operations(); if (i_step < 0 || static_cast(i_step) >= steps.size()) { proj_log_error(ctx, __FUNCTION__, "Invalid step index"); return nullptr; } return pj_obj_create(ctx, steps[i_step]); } proj-6.3.1/src/iso19111/coordinatesystem.cpp0000664000175000017500000013666213617003642015514 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/coordinatesystem.hpp" #include "proj/common.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/coordinatesystem_internal.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include #include #include #include #include using namespace NS_PROJ::internal; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace cs { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Meridian::Private { common::Angle longitude_{}; explicit Private(const common::Angle &longitude) : longitude_(longitude) {} }; //! @endcond // --------------------------------------------------------------------------- Meridian::Meridian(const common::Angle &longitudeIn) : d(internal::make_unique(longitudeIn)) {} // --------------------------------------------------------------------------- #ifdef notdef Meridian::Meridian(const Meridian &other) : IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Meridian::~Meridian() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the longitude of the meridian that the axis follows from the * pole. * * @return the longitude. */ const common::Angle &Meridian::longitude() PROJ_PURE_DEFN { return d->longitude_; } // --------------------------------------------------------------------------- /** \brief Instantiate a Meridian. * * @param longitudeIn longitude of the meridian that the axis follows from the * pole. * @return new Meridian. */ MeridianNNPtr Meridian::create(const common::Angle &longitudeIn) { return Meridian::nn_make_shared(longitudeIn); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Meridian::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { formatter->startNode(io::WKTConstants::MERIDIAN, !identifiers().empty()); formatter->add(longitude().value()); longitude().unit()._exportToWKT(formatter, io::WKTConstants::ANGLEUNIT); if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CoordinateSystemAxis::Private { std::string abbreviation{}; const AxisDirection *direction = &(AxisDirection::UNSPECIFIED); common::UnitOfMeasure unit{}; util::optional minimumValue{}; util::optional maximumValue{}; MeridianPtr meridian{}; // TODO rangeMeaning }; //! @endcond // --------------------------------------------------------------------------- CoordinateSystemAxis::CoordinateSystemAxis() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- #ifdef notdef CoordinateSystemAxis::CoordinateSystemAxis(const CoordinateSystemAxis &other) : IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateSystemAxis::~CoordinateSystemAxis() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the axis abbreviation. * * The abbreviation used for this coordinate system axis; this abbreviation * is also used to identify the coordinates in the coordinate tuple. * Examples are X and Y. * * @return the abbreviation. */ const std::string &CoordinateSystemAxis::abbreviation() PROJ_PURE_DEFN { return d->abbreviation; } // --------------------------------------------------------------------------- /** \brief Return the axis direction. * * The direction of this coordinate system axis (or in the case of Cartesian * projected coordinates, the direction of this coordinate system axis locally) * Examples: north or south, east or west, up or down. Within any set of * coordinate system axes, only one of each pair of terms can be used. For * Earth-fixed CRSs, this direction is often approximate and intended to * provide a human interpretable meaning to the axis. When a geodetic reference * frame is used, the precise directions of the axes may therefore vary * slightly from this approximate direction. Note that an EngineeringCRS often * requires specific descriptions of the directions of its coordinate system * axes. * * @return the direction. */ const AxisDirection &CoordinateSystemAxis::direction() PROJ_PURE_DEFN { return *(d->direction); } // --------------------------------------------------------------------------- /** \brief Return the axis unit. * * This is the spatial unit or temporal quantity used for this coordinate * system axis. The value of a coordinate in a coordinate tuple shall be * recorded using this unit. * * @return the axis unit. */ const common::UnitOfMeasure &CoordinateSystemAxis::unit() PROJ_PURE_DEFN { return d->unit; } // --------------------------------------------------------------------------- /** \brief Return the minimum value normally allowed for this axis, in the unit * for the axis. * * @return the minimum value, or empty. */ const util::optional & CoordinateSystemAxis::minimumValue() PROJ_PURE_DEFN { return d->minimumValue; } // --------------------------------------------------------------------------- /** \brief Return the maximum value normally allowed for this axis, in the unit * for the axis. * * @return the maximum value, or empty. */ const util::optional & CoordinateSystemAxis::maximumValue() PROJ_PURE_DEFN { return d->maximumValue; } // --------------------------------------------------------------------------- /** \brief Return the meridian that the axis follows from the pole, for a * coordinate * reference system centered on a pole. * * @return the meridian, or null. */ const MeridianPtr &CoordinateSystemAxis::meridian() PROJ_PURE_DEFN { return d->meridian; } // --------------------------------------------------------------------------- /** \brief Instantiate a CoordinateSystemAxis. * * @param properties See \ref general_properties. The name should generally be * defined. * @param abbreviationIn Axis abbreviation (might be empty) * @param directionIn Axis direction * @param unitIn Axis unit * @param meridianIn The meridian that the axis follows from the pole, for a * coordinate * reference system centered on a pole, or nullptr * @return a new CoordinateSystemAxis. */ CoordinateSystemAxisNNPtr CoordinateSystemAxis::create( const util::PropertyMap &properties, const std::string &abbreviationIn, const AxisDirection &directionIn, const common::UnitOfMeasure &unitIn, const MeridianPtr &meridianIn) { auto csa(CoordinateSystemAxis::nn_make_shared()); csa->setProperties(properties); csa->d->abbreviation = abbreviationIn; csa->d->direction = &directionIn; csa->d->unit = unitIn; csa->d->meridian = meridianIn; return csa; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateSystemAxis::_exportToWKT( // cppcheck-suppress passedByValue io::WKTFormatter *formatter) const // throw(FormattingException) { _exportToWKT(formatter, 0, false); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::string CoordinateSystemAxis::normalizeAxisName(const std::string &str) { if (str.empty()) { return str; } // on import, transform from WKT2 "longitude" to "Longitude", as in the // EPSG database. return toupper(str.substr(0, 1)) + str.substr(1); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateSystemAxis::_exportToWKT(io::WKTFormatter *formatter, int order, bool disableAbbrev) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(io::WKTConstants::AXIS, !identifiers().empty()); const std::string &axisName = nameStr(); const std::string &abbrev = abbreviation(); std::string parenthesizedAbbrev = "(" + abbrev + ")"; std::string dir = direction().toString(); std::string axisDesignation; // It seems that the convention in WKT2 for axis name is first letter in // lower case. Whereas in WKT1 GDAL, it is in upper case (as in the EPSG // database) if (!axisName.empty()) { if (isWKT2) { axisDesignation = tolower(axisName.substr(0, 1)) + axisName.substr(1); } else { if (axisName == "Geodetic latitude") { axisDesignation = "Latitude"; } else if (axisName == "Geodetic longitude") { axisDesignation = "Longitude"; } else { axisDesignation = axisName; } } } if (!disableAbbrev && isWKT2 && // For geodetic CS, export the axis name without abbreviation !(axisName == AxisName::Latitude || axisName == AxisName::Longitude)) { if (!axisDesignation.empty() && !abbrev.empty()) { axisDesignation += " "; } if (!abbrev.empty()) { axisDesignation += parenthesizedAbbrev; } } if (!isWKT2) { dir = toupper(dir); if (direction() == AxisDirection::GEOCENTRIC_Z) { dir = AxisDirectionWKT1::NORTH; } else if (AxisDirectionWKT1::valueOf(dir) == nullptr) { dir = AxisDirectionWKT1::OTHER; } } else if (!abbrev.empty()) { // For geocentric CS, just put the abbreviation if (direction() == AxisDirection::GEOCENTRIC_X || direction() == AxisDirection::GEOCENTRIC_Y || direction() == AxisDirection::GEOCENTRIC_Z) { axisDesignation = parenthesizedAbbrev; } // For cartesian CS with Easting/Northing, export only the abbreviation else if ((order == 1 && axisName == AxisName::Easting && abbrev == AxisAbbreviation::E) || (order == 2 && axisName == AxisName::Northing && abbrev == AxisAbbreviation::N)) { axisDesignation = parenthesizedAbbrev; } } formatter->addQuotedString(axisDesignation); formatter->add(dir); const auto &l_meridian = meridian(); if (isWKT2 && l_meridian) { l_meridian->_exportToWKT(formatter); } if (formatter->outputAxisOrder() && order > 0) { formatter->startNode(io::WKTConstants::ORDER, false); formatter->add(order); formatter->endNode(); } if (formatter->outputUnit() && unit().type() != common::UnitOfMeasure::Type::NONE) { unit()._exportToWKT(formatter); } if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateSystemAxis::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("Axis", !identifiers().empty())); writer.AddObjKey("name"); writer.Add(nameStr()); writer.AddObjKey("abbreviation"); writer.Add(abbreviation()); writer.AddObjKey("direction"); writer.Add(direction().toString()); const auto &l_unit(unit()); if (l_unit == common::UnitOfMeasure::METRE || l_unit == common::UnitOfMeasure::DEGREE) { writer.AddObjKey("unit"); writer.Add(l_unit.name()); } else if (l_unit.type() != common::UnitOfMeasure::Type::NONE) { writer.AddObjKey("unit"); l_unit._exportToJSON(formatter); } if (formatter->outputId()) { formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool CoordinateSystemAxis::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherCSA = dynamic_cast(other); if (otherCSA == nullptr) { return false; } // For approximate comparison, only care about axis direction and unit. if (!(*(d->direction) == *(otherCSA->d->direction) && d->unit._isEquivalentTo(otherCSA->d->unit, criterion))) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (abbreviation() != otherCSA->abbreviation()) { return false; } // TODO other metadata } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateSystemAxisNNPtr CoordinateSystemAxis::alterUnit(const common::UnitOfMeasure &newUnit) const { return create(util::PropertyMap().set(IdentifiedObject::NAME_KEY, name()), abbreviation(), direction(), newUnit, meridian()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CoordinateSystem::Private { std::vector axisList{}; explicit Private(const std::vector &axisListIn) : axisList(axisListIn) {} }; //! @endcond // --------------------------------------------------------------------------- CoordinateSystem::CoordinateSystem( const std::vector &axisIn) : d(internal::make_unique(axisIn)) {} // --------------------------------------------------------------------------- #ifdef notdef CoordinateSystem::CoordinateSystem(const CoordinateSystem &other) : IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateSystem::~CoordinateSystem() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the list of axes of this coordinate system. * * @return the axes. */ const std::vector & CoordinateSystem::axisList() PROJ_PURE_DEFN { return d->axisList; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateSystem::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { if (formatter->outputAxis() != io::WKTFormatter::OutputAxisRule::YES) { return; } const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const auto &l_axisList = axisList(); if (isWKT2) { formatter->startNode(io::WKTConstants::CS_, !identifiers().empty()); formatter->add(getWKT2Type(formatter->use2019Keywords())); formatter->add(static_cast(l_axisList.size())); formatter->endNode(); formatter->startNode(std::string(), false); // anonymous indentation level } common::UnitOfMeasure unit = common::UnitOfMeasure::NONE; bool bAllSameUnit = true; bool bFirstUnit = true; for (const auto &axis : l_axisList) { const auto &l_unit = axis->unit(); if (bFirstUnit) { unit = l_unit; bFirstUnit = false; } else if (unit != l_unit) { bAllSameUnit = false; } } formatter->pushOutputUnit( isWKT2 && (!bAllSameUnit || !formatter->outputCSUnitOnlyOnceIfSame())); int order = 1; const bool disableAbbrev = (l_axisList.size() == 3 && l_axisList[0]->nameStr() == AxisName::Latitude && l_axisList[1]->nameStr() == AxisName::Longitude && l_axisList[2]->nameStr() == AxisName::Ellipsoidal_height); for (auto &axis : l_axisList) { int axisOrder = (isWKT2 && l_axisList.size() > 1) ? order : 0; axis->_exportToWKT(formatter, axisOrder, disableAbbrev); order++; } if (isWKT2 && !l_axisList.empty() && bAllSameUnit && formatter->outputCSUnitOnlyOnceIfSame()) { unit._exportToWKT(formatter); } formatter->popOutputUnit(); if (isWKT2) { formatter->endNode(); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateSystem::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext("CoordinateSystem", !identifiers().empty())); writer.AddObjKey("subtype"); writer.Add(getWKT2Type(true)); writer.AddObjKey("axis"); { auto axisContext(writer.MakeArrayContext(false)); const auto &l_axisList = axisList(); for (auto &axis : l_axisList) { formatter->setOmitTypeInImmediateChild(); axis->_exportToJSON(formatter); } } if (formatter->outputId()) { formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool CoordinateSystem::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherCS = dynamic_cast(other); if (otherCS == nullptr || !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } const auto &list = axisList(); const auto &otherList = otherCS->axisList(); if (list.size() != otherList.size()) { return false; } if (getWKT2Type(true) != otherCS->getWKT2Type(true)) { return false; } for (size_t i = 0; i < list.size(); i++) { if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion, dbContext)) { return false; } } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress SphericalCS::~SphericalCS() = default; //! @endcond // --------------------------------------------------------------------------- SphericalCS::SphericalCS(const std::vector &axisIn) : CoordinateSystem(axisIn) {} // --------------------------------------------------------------------------- #ifdef notdef SphericalCS::SphericalCS(const SphericalCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a SphericalCS. * * @param properties See \ref general_properties. * @param axis1 The first axis. * @param axis2 The second axis. * @param axis3 The third axis. * @return a new SphericalCS. */ SphericalCSNNPtr SphericalCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis1, const CoordinateSystemAxisNNPtr &axis2, const CoordinateSystemAxisNNPtr &axis3) { std::vector axis{axis1, axis2, axis3}; auto cs(SphericalCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress EllipsoidalCS::~EllipsoidalCS() = default; //! @endcond // --------------------------------------------------------------------------- EllipsoidalCS::EllipsoidalCS( const std::vector &axisIn) : CoordinateSystem(axisIn) {} // --------------------------------------------------------------------------- #ifdef notdef EllipsoidalCS::EllipsoidalCS(const EllipsoidalCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS. * * @param properties See \ref general_properties. * @param axis1 The first axis. * @param axis2 The second axis. * @return a new EllipsoidalCS. */ EllipsoidalCSNNPtr EllipsoidalCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis1, const CoordinateSystemAxisNNPtr &axis2) { std::vector axis{axis1, axis2}; auto cs(EllipsoidalCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS. * * @param properties See \ref general_properties. * @param axis1 The first axis. * @param axis2 The second axis. * @param axis3 The third axis. * @return a new EllipsoidalCS. */ EllipsoidalCSNNPtr EllipsoidalCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis1, const CoordinateSystemAxisNNPtr &axis2, const CoordinateSystemAxisNNPtr &axis3) { std::vector axis{axis1, axis2, axis3}; auto cs(EllipsoidalCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateSystemAxisNNPtr CoordinateSystemAxis::createLAT_NORTH(const common::UnitOfMeasure &unit) { return create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Latitude), AxisAbbreviation::lat, AxisDirection::NORTH, unit); } CoordinateSystemAxisNNPtr CoordinateSystemAxis::createLONG_EAST(const common::UnitOfMeasure &unit) { return create(util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Longitude), AxisAbbreviation::lon, AxisDirection::EAST, unit); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS with a Latitude (first) and Longitude * (second) axis. * * @param unit Angular unit of the axes. * @return a new EllipsoidalCS. */ EllipsoidalCSNNPtr EllipsoidalCS::createLatitudeLongitude(const common::UnitOfMeasure &unit) { return EllipsoidalCS::create(util::PropertyMap(), CoordinateSystemAxis::createLAT_NORTH(unit), CoordinateSystemAxis::createLONG_EAST(unit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS with a Latitude (first), Longitude * (second) axis and ellipsoidal height (third) axis. * * @param angularUnit Angular unit of the latitude and longitude axes. * @param linearUnit Linear unit of the ellipsoidal height axis. * @return a new EllipsoidalCS. */ EllipsoidalCSNNPtr EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight( const common::UnitOfMeasure &angularUnit, const common::UnitOfMeasure &linearUnit) { return EllipsoidalCS::create( util::PropertyMap(), CoordinateSystemAxis::createLAT_NORTH(angularUnit), CoordinateSystemAxis::createLONG_EAST(angularUnit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Ellipsoidal_height), AxisAbbreviation::h, AxisDirection::UP, linearUnit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS with a Longitude (first) and Latitude * (second) axis. * * @param unit Angular unit of the axes. * @return a new EllipsoidalCS. */ EllipsoidalCSNNPtr EllipsoidalCS::createLongitudeLatitude(const common::UnitOfMeasure &unit) { return EllipsoidalCS::create(util::PropertyMap(), CoordinateSystemAxis::createLONG_EAST(unit), CoordinateSystemAxis::createLAT_NORTH(unit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a EllipsoidalCS with a Longitude (first), Latitude * (second) axis and ellipsoidal height (third) axis. * * @param angularUnit Angular unit of the latitude and longitude axes. * @param linearUnit Linear unit of the ellipsoidal height axis. * @return a new EllipsoidalCS. * @since 7.0 */ EllipsoidalCSNNPtr EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight( const common::UnitOfMeasure &angularUnit, const common::UnitOfMeasure &linearUnit) { return EllipsoidalCS::create( util::PropertyMap(), CoordinateSystemAxis::createLONG_EAST(angularUnit), CoordinateSystemAxis::createLAT_NORTH(angularUnit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Ellipsoidal_height), AxisAbbreviation::h, AxisDirection::UP, linearUnit)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress /** \brief Return the axis order in an enumerated way. */ EllipsoidalCS::AxisOrder EllipsoidalCS::axisOrder() const { const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; const auto &dir0 = l_axisList[0]->direction(); const auto &dir1 = l_axisList[1]->direction(); if (&dir0 == &AxisDirection::NORTH && &dir1 == &AxisDirection::EAST) { if (l_axisList.size() == 2) { return AxisOrder::LAT_NORTH_LONG_EAST; } else if (&l_axisList[2]->direction() == &AxisDirection::UP) { return AxisOrder::LAT_NORTH_LONG_EAST_HEIGHT_UP; } } else if (&dir0 == &AxisDirection::EAST && &dir1 == &AxisDirection::NORTH) { if (l_axisList.size() == 2) { return AxisOrder::LONG_EAST_LAT_NORTH; } else if (&l_axisList[2]->direction() == &AxisDirection::UP) { return AxisOrder::LONG_EAST_LAT_NORTH_HEIGHT_UP; } } return AxisOrder::OTHER; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress EllipsoidalCSNNPtr EllipsoidalCS::alterAngularUnit( const common::UnitOfMeasure &angularUnit) const { const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; if (l_axisList.size() == 2) { return EllipsoidalCS::create(util::PropertyMap(), l_axisList[0]->alterUnit(angularUnit), l_axisList[1]->alterUnit(angularUnit)); } else { assert(l_axisList.size() == 3); return EllipsoidalCS::create( util::PropertyMap(), l_axisList[0]->alterUnit(angularUnit), l_axisList[1]->alterUnit(angularUnit), l_axisList[2]); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress EllipsoidalCSNNPtr EllipsoidalCS::alterLinearUnit(const common::UnitOfMeasure &linearUnit) const { const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; if (l_axisList.size() == 2) { return EllipsoidalCS::create(util::PropertyMap(), l_axisList[0], l_axisList[1]); } else { assert(l_axisList.size() == 3); return EllipsoidalCS::create(util::PropertyMap(), l_axisList[0], l_axisList[1], l_axisList[2]->alterUnit(linearUnit)); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalCS::~VerticalCS() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalCS::VerticalCS(const CoordinateSystemAxisNNPtr &axisIn) : CoordinateSystem(std::vector{axisIn}) {} //! @endcond // --------------------------------------------------------------------------- #ifdef notdef VerticalCS::VerticalCS(const VerticalCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalCS. * * @param properties See \ref general_properties. * @param axis The axis. * @return a new VerticalCS. */ VerticalCSNNPtr VerticalCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis) { auto cs(VerticalCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalCS with a Gravity-related height axis * * @param unit linear unit. * @return a new VerticalCS. */ VerticalCSNNPtr VerticalCS::createGravityRelatedHeight(const common::UnitOfMeasure &unit) { auto cs(VerticalCS::nn_make_shared(CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, "Gravity-related height"), "H", AxisDirection::UP, unit))); return cs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalCSNNPtr VerticalCS::alterUnit(const common::UnitOfMeasure &unit) const { const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; return VerticalCS::nn_make_shared( l_axisList[0]->alterUnit(unit)); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CartesianCS::~CartesianCS() = default; //! @endcond // --------------------------------------------------------------------------- CartesianCS::CartesianCS(const std::vector &axisIn) : CoordinateSystem(axisIn) {} // --------------------------------------------------------------------------- #ifdef notdef CartesianCS::CartesianCS(const CartesianCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS. * * @param properties See \ref general_properties. * @param axis1 The first axis. * @param axis2 The second axis. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis1, const CoordinateSystemAxisNNPtr &axis2) { std::vector axis{axis1, axis2}; auto cs(CartesianCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS. * * @param properties See \ref general_properties. * @param axis1 The first axis. * @param axis2 The second axis. * @param axis3 The third axis. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axis1, const CoordinateSystemAxisNNPtr &axis2, const CoordinateSystemAxisNNPtr &axis3) { std::vector axis{axis1, axis2, axis3}; auto cs(CartesianCS::nn_make_shared(axis)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS with a Easting (first) and Northing * (second) axis. * * @param unit Linear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createEastingNorthing(const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Easting), AxisAbbreviation::E, AxisDirection::EAST, unit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Northing), AxisAbbreviation::N, AxisDirection::NORTH, unit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS with a Northing (first) and Easting * (second) axis. * * @param unit Linear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createNorthingEasting(const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Northing), AxisAbbreviation::N, AxisDirection::NORTH, unit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Easting), AxisAbbreviation::E, AxisDirection::EAST, unit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS with a Westing (first) and Southing * (second) axis. * * @param unit Linear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createWestingSouthing(const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Easting), AxisAbbreviation::Y, AxisDirection::WEST, unit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Northing), AxisAbbreviation::X, AxisDirection::SOUTH, unit)); } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS, north-pole centered, * with a Easting (first) South-Oriented and * Northing (second) South-Oriented axis. * * @param unit Linear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createNorthPoleEastingSouthNorthingSouth( const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Easting), AxisAbbreviation::E, AxisDirection::SOUTH, unit, Meridian::create(common::Angle(90))), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Northing), AxisAbbreviation::N, AxisDirection::SOUTH, unit, Meridian::create(common::Angle(180)))); } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS, south-pole centered, * with a Easting (first) North-Oriented and * Northing (second) North-Oriented axis. * * @param unit Linear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createSouthPoleEastingNorthNorthingNorth( const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Easting), AxisAbbreviation::E, AxisDirection::NORTH, unit, Meridian::create(common::Angle(90))), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Northing), AxisAbbreviation::N, AxisDirection::NORTH, unit, Meridian::create(common::Angle(0)))); } // --------------------------------------------------------------------------- /** \brief Instantiate a CartesianCS with the three geocentric axes. * * @param unit Liinear unit of the axes. * @return a new CartesianCS. */ CartesianCSNNPtr CartesianCS::createGeocentric(const common::UnitOfMeasure &unit) { return create(util::PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Geocentric_X), AxisAbbreviation::X, AxisDirection::GEOCENTRIC_X, unit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Geocentric_Y), AxisAbbreviation::Y, AxisDirection::GEOCENTRIC_Y, unit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Geocentric_Z), AxisAbbreviation::Z, AxisDirection::GEOCENTRIC_Z, unit)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CartesianCSNNPtr CartesianCS::alterUnit(const common::UnitOfMeasure &unit) const { const auto &l_axisList = CoordinateSystem::getPrivate()->axisList; if (l_axisList.size() == 2) { return CartesianCS::create(util::PropertyMap(), l_axisList[0]->alterUnit(unit), l_axisList[1]->alterUnit(unit)); } else { assert(l_axisList.size() == 3); return CartesianCS::create( util::PropertyMap(), l_axisList[0]->alterUnit(unit), l_axisList[1]->alterUnit(unit), l_axisList[2]->alterUnit(unit)); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress OrdinalCS::~OrdinalCS() = default; //! @endcond // --------------------------------------------------------------------------- OrdinalCS::OrdinalCS(const std::vector &axisIn) : CoordinateSystem(axisIn) {} // --------------------------------------------------------------------------- #ifdef notdef OrdinalCS::OrdinalCS(const OrdinalCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a OrdinalCS. * * @param properties See \ref general_properties. * @param axisIn List of axis. * @return a new OrdinalCS. */ OrdinalCSNNPtr OrdinalCS::create(const util::PropertyMap &properties, const std::vector &axisIn) { auto cs(OrdinalCS::nn_make_shared(axisIn)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ParametricCS::~ParametricCS() = default; //! @endcond // --------------------------------------------------------------------------- ParametricCS::ParametricCS(const std::vector &axisIn) : CoordinateSystem(axisIn) {} // --------------------------------------------------------------------------- #ifdef notdef ParametricCS::ParametricCS(const ParametricCS &) = default; #endif // --------------------------------------------------------------------------- /** \brief Instantiate a ParametricCS. * * @param properties See \ref general_properties. * @param axisIn Axis. * @return a new ParametricCS. */ ParametricCSNNPtr ParametricCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axisIn) { auto cs(ParametricCS::nn_make_shared( std::vector{axisIn})); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- AxisDirection::AxisDirection(const std::string &nameIn) : CodeList(nameIn) { assert(registry.find(nameIn) == registry.end()); registry[nameIn] = this; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const AxisDirection * AxisDirection::valueOf(const std::string &nameIn) noexcept { auto iter = registry.find(nameIn); if (iter == registry.end()) return nullptr; return iter->second; } //! @endcond //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- AxisDirectionWKT1::AxisDirectionWKT1(const std::string &nameIn) : CodeList(nameIn) { assert(registry.find(nameIn) == registry.end()); registry[nameIn] = this; } // --------------------------------------------------------------------------- const AxisDirectionWKT1 *AxisDirectionWKT1::valueOf(const std::string &nameIn) { auto iter = registry.find(nameIn); if (iter == registry.end()) return nullptr; return iter->second; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalCS::~TemporalCS() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalCS::TemporalCS(const CoordinateSystemAxisNNPtr &axisIn) : CoordinateSystem(std::vector{axisIn}) {} //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DateTimeTemporalCS::~DateTimeTemporalCS() = default; //! @endcond // --------------------------------------------------------------------------- DateTimeTemporalCS::DateTimeTemporalCS(const CoordinateSystemAxisNNPtr &axisIn) : TemporalCS(axisIn) {} // --------------------------------------------------------------------------- /** \brief Instantiate a DateTimeTemporalCS. * * @param properties See \ref general_properties. * @param axisIn The axis. * @return a new DateTimeTemporalCS. */ DateTimeTemporalCSNNPtr DateTimeTemporalCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axisIn) { auto cs(DateTimeTemporalCS::nn_make_shared(axisIn)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- std::string DateTimeTemporalCS::getWKT2Type(bool use2019Keywords) const { return use2019Keywords ? "TemporalDateTime" : "temporal"; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalCountCS::~TemporalCountCS() = default; //! @endcond // --------------------------------------------------------------------------- TemporalCountCS::TemporalCountCS(const CoordinateSystemAxisNNPtr &axisIn) : TemporalCS(axisIn) {} // --------------------------------------------------------------------------- /** \brief Instantiate a TemporalCountCS. * * @param properties See \ref general_properties. * @param axisIn The axis. * @return a new TemporalCountCS. */ TemporalCountCSNNPtr TemporalCountCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axisIn) { auto cs(TemporalCountCS::nn_make_shared(axisIn)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- std::string TemporalCountCS::getWKT2Type(bool use2019Keywords) const { return use2019Keywords ? "TemporalCount" : "temporal"; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalMeasureCS::~TemporalMeasureCS() = default; //! @endcond // --------------------------------------------------------------------------- TemporalMeasureCS::TemporalMeasureCS(const CoordinateSystemAxisNNPtr &axisIn) : TemporalCS(axisIn) {} // --------------------------------------------------------------------------- /** \brief Instantiate a TemporalMeasureCS. * * @param properties See \ref general_properties. * @param axisIn The axis. * @return a new TemporalMeasureCS. */ TemporalMeasureCSNNPtr TemporalMeasureCS::create(const util::PropertyMap &properties, const CoordinateSystemAxisNNPtr &axisIn) { auto cs(TemporalMeasureCS::nn_make_shared(axisIn)); cs->setProperties(properties); return cs; } // --------------------------------------------------------------------------- std::string TemporalMeasureCS::getWKT2Type(bool use2019Keywords) const { return use2019Keywords ? "TemporalMeasure" : "temporal"; } } // namespace cs NS_PROJ_END proj-6.3.1/src/iso19111/io.cpp0000664000175000017500000134625213620030240012512 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include #include #include #include #include #include #include #include #include #include // std::istringstream #include #include #include #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/coordinateoperation_internal.hpp" #include "proj/internal/coordinatesystem_internal.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj/internal/include_nlohmann_json.hpp" #include "proj_constants.h" #include "proj_json_streaming_writer.hpp" #include "wkt1_parser.h" #include "wkt2_parser.h" // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_internal.h" #include "proj_api.h" // clang-format on using namespace NS_PROJ::common; using namespace NS_PROJ::crs; using namespace NS_PROJ::cs; using namespace NS_PROJ::datum; using namespace NS_PROJ::internal; using namespace NS_PROJ::metadata; using namespace NS_PROJ::operation; using namespace NS_PROJ::util; using json = nlohmann::json; //! @cond Doxygen_Suppress static const std::string emptyString{}; // If changing that value, change it in data/projjson.schema.json as well #define PROJJSON_CURRENT_VERSION \ "https://proj.org/schemas/v0.2/projjson.schema.json" //! @endcond #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn>::~nn() = default; template<> nn > >::~nn() = default; template<> nn > >::~nn() = default; template<> nn > >::~nn() = default; }} #endif NS_PROJ_START namespace io { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress IWKTExportable::~IWKTExportable() = default; // --------------------------------------------------------------------------- std::string IWKTExportable::exportToWKT(WKTFormatter *formatter) const { _exportToWKT(formatter); return formatter->toString(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct WKTFormatter::Private { struct Params { WKTFormatter::Convention convention_ = WKTFormatter::Convention::WKT2; WKTFormatter::Version version_ = WKTFormatter::Version::WKT2; bool multiLine_ = true; bool strict_ = true; int indentWidth_ = 4; bool idOnTopLevelOnly_ = false; bool outputAxisOrder_ = false; bool primeMeridianOmittedIfGreenwich_ = false; bool ellipsoidUnitOmittedIfMetre_ = false; bool primeMeridianOrParameterUnitOmittedIfSameAsAxis_ = false; bool forceUNITKeyword_ = false; bool outputCSUnitOnlyOnceIfSame_ = false; bool primeMeridianInDegree_ = false; bool use2019Keywords_ = false; bool useESRIDialect_ = false; OutputAxisRule outputAxis_ = WKTFormatter::OutputAxisRule::YES; }; Params params_{}; DatabaseContextPtr dbContext_{}; int indentLevel_ = 0; int level_ = 0; std::vector stackHasChild_{}; std::vector stackHasId_{false}; std::vector stackEmptyKeyword_{}; std::vector stackDisableUsage_{}; std::vector outputUnitStack_{true}; std::vector outputIdStack_{true}; std::vector axisLinearUnitStack_{ util::nn_make_shared(UnitOfMeasure::METRE)}; std::vector axisAngularUnitStack_{ util::nn_make_shared(UnitOfMeasure::DEGREE)}; bool abridgedTransformation_ = false; bool useDerivingConversion_ = false; std::vector toWGS84Parameters_{}; std::string hDatumExtension_{}; std::string vDatumExtension_{}; std::vector inversionStack_{false}; std::string result_{}; // cppcheck-suppress functionStatic void addNewLine(); void addIndentation(); // cppcheck-suppress functionStatic void startNewChild(); }; //! @endcond // --------------------------------------------------------------------------- /** \brief Constructs a new formatter. * * A formatter can be used only once (its internal state is mutated) * * Its default behaviour can be adjusted with the different setters. * * @param convention WKT flavor. Defaults to Convention::WKT2 * @param dbContext Database context, to allow queries in it if needed. * This is used for example for WKT1_ESRI output to do name substitutions. * * @return new formatter. */ WKTFormatterNNPtr WKTFormatter::create(Convention convention, // cppcheck-suppress passedByValue DatabaseContextPtr dbContext) { auto ret = NN_NO_CHECK(WKTFormatter::make_unique(convention)); ret->d->dbContext_ = dbContext; return ret; } // --------------------------------------------------------------------------- /** \brief Constructs a new formatter from another one. * * A formatter can be used only once (its internal state is mutated) * * Its default behaviour can be adjusted with the different setters. * * @param other source formatter. * @return new formatter. */ WKTFormatterNNPtr WKTFormatter::create(const WKTFormatterNNPtr &other) { auto f = create(other->d->params_.convention_, other->d->dbContext_); f->d->params_ = other->d->params_; return f; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress WKTFormatter::~WKTFormatter() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Whether to use multi line output or not. */ WKTFormatter &WKTFormatter::setMultiLine(bool multiLine) noexcept { d->params_.multiLine_ = multiLine; return *this; } // --------------------------------------------------------------------------- /** \brief Set number of spaces for each indentation level (defaults to 4). */ WKTFormatter &WKTFormatter::setIndentationWidth(int width) noexcept { d->params_.indentWidth_ = width; return *this; } // --------------------------------------------------------------------------- /** \brief Set whether AXIS nodes should be output. */ WKTFormatter & WKTFormatter::setOutputAxis(OutputAxisRule outputAxisIn) noexcept { d->params_.outputAxis_ = outputAxisIn; return *this; } // --------------------------------------------------------------------------- /** \brief Set whether the formatter should operate on strict more or not. * * The default is strict mode, in which case a FormattingException can be * thrown. */ WKTFormatter &WKTFormatter::setStrict(bool strictIn) noexcept { d->params_.strict_ = strictIn; return *this; } // --------------------------------------------------------------------------- /** \brief Returns whether the formatter is in strict mode. */ bool WKTFormatter::isStrict() const noexcept { return d->params_.strict_; } // --------------------------------------------------------------------------- /** Returns the WKT string from the formatter. */ const std::string &WKTFormatter::toString() const { if (d->indentLevel_ > 0 || d->level_ > 0) { // For intermediary nodes, the formatter is in a inconsistent // state. throw FormattingException("toString() called on intermediate nodes"); } if (d->axisLinearUnitStack_.size() != 1) throw FormattingException( "Unbalanced pushAxisLinearUnit() / popAxisLinearUnit()"); if (d->axisAngularUnitStack_.size() != 1) throw FormattingException( "Unbalanced pushAxisAngularUnit() / popAxisAngularUnit()"); if (d->outputIdStack_.size() != 1) throw FormattingException("Unbalanced pushOutputId() / popOutputId()"); if (d->outputUnitStack_.size() != 1) throw FormattingException( "Unbalanced pushOutputUnit() / popOutputUnit()"); if (d->stackHasId_.size() != 1) throw FormattingException("Unbalanced pushHasId() / popHasId()"); if (!d->stackDisableUsage_.empty()) throw FormattingException( "Unbalanced pushDisableUsage() / popDisableUsage()"); return d->result_; } //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- WKTFormatter::WKTFormatter(Convention convention) : d(internal::make_unique()) { d->params_.convention_ = convention; switch (convention) { case Convention::WKT2_2019: d->params_.use2019Keywords_ = true; PROJ_FALLTHROUGH case Convention::WKT2: d->params_.version_ = WKTFormatter::Version::WKT2; d->params_.outputAxisOrder_ = true; break; case Convention::WKT2_2019_SIMPLIFIED: d->params_.use2019Keywords_ = true; PROJ_FALLTHROUGH case Convention::WKT2_SIMPLIFIED: d->params_.version_ = WKTFormatter::Version::WKT2; d->params_.idOnTopLevelOnly_ = true; d->params_.outputAxisOrder_ = false; d->params_.primeMeridianOmittedIfGreenwich_ = true; d->params_.ellipsoidUnitOmittedIfMetre_ = true; d->params_.primeMeridianOrParameterUnitOmittedIfSameAsAxis_ = true; d->params_.forceUNITKeyword_ = true; d->params_.outputCSUnitOnlyOnceIfSame_ = true; break; case Convention::WKT1_GDAL: d->params_.version_ = WKTFormatter::Version::WKT1; d->params_.outputAxisOrder_ = false; d->params_.forceUNITKeyword_ = true; d->params_.primeMeridianInDegree_ = true; d->params_.outputAxis_ = WKTFormatter::OutputAxisRule::WKT1_GDAL_EPSG_STYLE; break; case Convention::WKT1_ESRI: d->params_.version_ = WKTFormatter::Version::WKT1; d->params_.outputAxisOrder_ = false; d->params_.forceUNITKeyword_ = true; d->params_.primeMeridianInDegree_ = true; d->params_.useESRIDialect_ = true; d->params_.multiLine_ = false; d->params_.outputAxis_ = WKTFormatter::OutputAxisRule::NO; break; default: assert(false); break; } } // --------------------------------------------------------------------------- WKTFormatter &WKTFormatter::setOutputId(bool outputIdIn) { if (d->indentLevel_ != 0) { throw Exception( "setOutputId() shall only be called when the stack state is empty"); } d->outputIdStack_[0] = outputIdIn; return *this; } // --------------------------------------------------------------------------- void WKTFormatter::Private::addNewLine() { result_ += '\n'; } // --------------------------------------------------------------------------- void WKTFormatter::Private::addIndentation() { result_ += std::string(indentLevel_ * params_.indentWidth_, ' '); } // --------------------------------------------------------------------------- void WKTFormatter::enter() { if (d->indentLevel_ == 0 && d->level_ == 0) { d->stackHasChild_.push_back(false); } ++d->level_; } // --------------------------------------------------------------------------- void WKTFormatter::leave() { assert(d->level_ > 0); --d->level_; if (d->indentLevel_ == 0 && d->level_ == 0) { d->stackHasChild_.pop_back(); } } // --------------------------------------------------------------------------- void WKTFormatter::startNode(const std::string &keyword, bool hasId) { if (!d->stackHasChild_.empty()) { d->startNewChild(); } else if (!d->result_.empty()) { d->result_ += ','; if (d->params_.multiLine_ && !keyword.empty()) { d->addNewLine(); } } if (d->params_.multiLine_) { if ((d->indentLevel_ || d->level_) && !keyword.empty()) { if (!d->result_.empty()) { d->addNewLine(); } d->addIndentation(); } } if (!keyword.empty()) { d->result_ += keyword; d->result_ += '['; } d->indentLevel_++; d->stackHasChild_.push_back(false); d->stackEmptyKeyword_.push_back(keyword.empty()); // Starting from a node that has a ID, we should emit ID nodes for : // - this node // - and for METHOD&PARAMETER nodes in WKT2, unless idOnTopLevelOnly_ is // set. // For WKT2, all other intermediate nodes shouldn't have ID ("not // recommended") if (!d->params_.idOnTopLevelOnly_ && d->indentLevel_ >= 2 && d->params_.version_ == WKTFormatter::Version::WKT2 && (keyword == WKTConstants::METHOD || keyword == WKTConstants::PARAMETER)) { pushOutputId(d->outputIdStack_[0]); } else if (d->indentLevel_ >= 2 && d->params_.version_ == WKTFormatter::Version::WKT2) { pushOutputId(d->outputIdStack_[0] && !d->stackHasId_.back()); } else { pushOutputId(outputId()); } d->stackHasId_.push_back(hasId || d->stackHasId_.back()); } // --------------------------------------------------------------------------- void WKTFormatter::endNode() { assert(d->indentLevel_ > 0); d->stackHasId_.pop_back(); popOutputId(); d->indentLevel_--; bool emptyKeyword = d->stackEmptyKeyword_.back(); d->stackEmptyKeyword_.pop_back(); d->stackHasChild_.pop_back(); if (!emptyKeyword) d->result_ += ']'; } // --------------------------------------------------------------------------- WKTFormatter &WKTFormatter::simulCurNodeHasId() { d->stackHasId_.back() = true; return *this; } // --------------------------------------------------------------------------- void WKTFormatter::Private::startNewChild() { assert(!stackHasChild_.empty()); if (stackHasChild_.back()) { result_ += ','; } stackHasChild_.back() = true; } // --------------------------------------------------------------------------- void WKTFormatter::addQuotedString(const char *str) { addQuotedString(std::string(str)); } void WKTFormatter::addQuotedString(const std::string &str) { d->startNewChild(); d->result_ += '"'; d->result_ += replaceAll(str, "\"", "\"\""); d->result_ += '"'; } // --------------------------------------------------------------------------- void WKTFormatter::add(const std::string &str) { d->startNewChild(); d->result_ += str; } // --------------------------------------------------------------------------- void WKTFormatter::add(int number) { d->startNewChild(); d->result_ += internal::toString(number); } // --------------------------------------------------------------------------- #ifdef __MINGW32__ static std::string normalizeExponent(const std::string &in) { // mingw will output 1e-0xy instead of 1e-xy. Fix that auto pos = in.find("e-0"); if (pos == std::string::npos) { return in; } if (pos + 4 < in.size() && isdigit(in[pos + 3]) && isdigit(in[pos + 4])) { return in.substr(0, pos + 2) + in.substr(pos + 3); } return in; } #else static inline std::string normalizeExponent(const std::string &in) { return in; } #endif static inline std::string normalizeSerializedString(const std::string &in) { auto ret(normalizeExponent(in)); return ret; } // --------------------------------------------------------------------------- void WKTFormatter::add(double number, int precision) { d->startNewChild(); if (number == 0.0) { if (d->params_.useESRIDialect_) { d->result_ += "0.0"; } else { d->result_ += '0'; } } else { std::string val( normalizeSerializedString(internal::toString(number, precision))); d->result_ += replaceAll(val, "e", "E"); if (d->params_.useESRIDialect_ && val.find('.') == std::string::npos) { d->result_ += ".0"; } } } // --------------------------------------------------------------------------- void WKTFormatter::pushOutputUnit(bool outputUnitIn) { d->outputUnitStack_.push_back(outputUnitIn); } // --------------------------------------------------------------------------- void WKTFormatter::popOutputUnit() { d->outputUnitStack_.pop_back(); } // --------------------------------------------------------------------------- bool WKTFormatter::outputUnit() const { return d->outputUnitStack_.back(); } // --------------------------------------------------------------------------- void WKTFormatter::pushOutputId(bool outputIdIn) { d->outputIdStack_.push_back(outputIdIn); } // --------------------------------------------------------------------------- void WKTFormatter::popOutputId() { d->outputIdStack_.pop_back(); } // --------------------------------------------------------------------------- bool WKTFormatter::outputId() const { return !d->params_.useESRIDialect_ && d->outputIdStack_.back(); } // --------------------------------------------------------------------------- void WKTFormatter::pushHasId(bool hasId) { d->stackHasId_.push_back(hasId); } // --------------------------------------------------------------------------- void WKTFormatter::popHasId() { d->stackHasId_.pop_back(); } // --------------------------------------------------------------------------- void WKTFormatter::pushDisableUsage() { d->stackDisableUsage_.push_back(true); } // --------------------------------------------------------------------------- void WKTFormatter::popDisableUsage() { d->stackDisableUsage_.pop_back(); } // --------------------------------------------------------------------------- bool WKTFormatter::outputUsage() const { return outputId() && d->stackDisableUsage_.empty(); } // --------------------------------------------------------------------------- void WKTFormatter::pushAxisLinearUnit(const UnitOfMeasureNNPtr &unit) { d->axisLinearUnitStack_.push_back(unit); } // --------------------------------------------------------------------------- void WKTFormatter::popAxisLinearUnit() { d->axisLinearUnitStack_.pop_back(); } // --------------------------------------------------------------------------- const UnitOfMeasureNNPtr &WKTFormatter::axisLinearUnit() const { return d->axisLinearUnitStack_.back(); } // --------------------------------------------------------------------------- void WKTFormatter::pushAxisAngularUnit(const UnitOfMeasureNNPtr &unit) { d->axisAngularUnitStack_.push_back(unit); } // --------------------------------------------------------------------------- void WKTFormatter::popAxisAngularUnit() { d->axisAngularUnitStack_.pop_back(); } // --------------------------------------------------------------------------- const UnitOfMeasureNNPtr &WKTFormatter::axisAngularUnit() const { return d->axisAngularUnitStack_.back(); } // --------------------------------------------------------------------------- WKTFormatter::OutputAxisRule WKTFormatter::outputAxis() const { return d->params_.outputAxis_; } // --------------------------------------------------------------------------- bool WKTFormatter::outputAxisOrder() const { return d->params_.outputAxisOrder_; } // --------------------------------------------------------------------------- bool WKTFormatter::primeMeridianOmittedIfGreenwich() const { return d->params_.primeMeridianOmittedIfGreenwich_; } // --------------------------------------------------------------------------- bool WKTFormatter::ellipsoidUnitOmittedIfMetre() const { return d->params_.ellipsoidUnitOmittedIfMetre_; } // --------------------------------------------------------------------------- bool WKTFormatter::primeMeridianOrParameterUnitOmittedIfSameAsAxis() const { return d->params_.primeMeridianOrParameterUnitOmittedIfSameAsAxis_; } // --------------------------------------------------------------------------- bool WKTFormatter::outputCSUnitOnlyOnceIfSame() const { return d->params_.outputCSUnitOnlyOnceIfSame_; } // --------------------------------------------------------------------------- bool WKTFormatter::forceUNITKeyword() const { return d->params_.forceUNITKeyword_; } // --------------------------------------------------------------------------- bool WKTFormatter::primeMeridianInDegree() const { return d->params_.primeMeridianInDegree_; } // --------------------------------------------------------------------------- bool WKTFormatter::idOnTopLevelOnly() const { return d->params_.idOnTopLevelOnly_; } // --------------------------------------------------------------------------- bool WKTFormatter::topLevelHasId() const { return d->stackHasId_.size() >= 2 && d->stackHasId_[1]; } // --------------------------------------------------------------------------- WKTFormatter::Version WKTFormatter::version() const { return d->params_.version_; } // --------------------------------------------------------------------------- bool WKTFormatter::use2019Keywords() const { return d->params_.use2019Keywords_; } // --------------------------------------------------------------------------- bool WKTFormatter::useESRIDialect() const { return d->params_.useESRIDialect_; } // --------------------------------------------------------------------------- const DatabaseContextPtr &WKTFormatter::databaseContext() const { return d->dbContext_; } // --------------------------------------------------------------------------- void WKTFormatter::setAbridgedTransformation(bool outputIn) { d->abridgedTransformation_ = outputIn; } // --------------------------------------------------------------------------- bool WKTFormatter::abridgedTransformation() const { return d->abridgedTransformation_; } // --------------------------------------------------------------------------- void WKTFormatter::setUseDerivingConversion(bool useDerivingConversionIn) { d->useDerivingConversion_ = useDerivingConversionIn; } // --------------------------------------------------------------------------- bool WKTFormatter::useDerivingConversion() const { return d->useDerivingConversion_; } // --------------------------------------------------------------------------- void WKTFormatter::setTOWGS84Parameters(const std::vector ¶ms) { d->toWGS84Parameters_ = params; } // --------------------------------------------------------------------------- const std::vector &WKTFormatter::getTOWGS84Parameters() const { return d->toWGS84Parameters_; } // --------------------------------------------------------------------------- void WKTFormatter::setVDatumExtension(const std::string &filename) { d->vDatumExtension_ = filename; } // --------------------------------------------------------------------------- const std::string &WKTFormatter::getVDatumExtension() const { return d->vDatumExtension_; } // --------------------------------------------------------------------------- void WKTFormatter::setHDatumExtension(const std::string &filename) { d->hDatumExtension_ = filename; } // --------------------------------------------------------------------------- const std::string &WKTFormatter::getHDatumExtension() const { return d->hDatumExtension_; } // --------------------------------------------------------------------------- std::string WKTFormatter::morphNameToESRI(const std::string &name) { for (const auto *suffix : {"(m)", "(ftUS)", "(E-N)", "(N-E)"}) { if (ends_with(name, suffix)) { return morphNameToESRI( name.substr(0, name.size() - strlen(suffix))) + suffix; } } std::string ret; bool insertUnderscore = false; // Replace any special character by underscore, except at the beginning // and of the name where those characters are removed. for (char ch : name) { if (ch == '+' || ch == '-' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { if (insertUnderscore && !ret.empty()) { ret += '_'; } ret += ch; insertUnderscore = false; } else { insertUnderscore = true; } } return ret; } // --------------------------------------------------------------------------- void WKTFormatter::ingestWKTNode(const WKTNodeNNPtr &node) { startNode(node->value(), true); for (const auto &child : node->children()) { if (!child->children().empty()) { ingestWKTNode(child); } else { add(child->value()); } } endNode(); } #ifdef unused // --------------------------------------------------------------------------- void WKTFormatter::startInversion() { d->inversionStack_.push_back(!d->inversionStack_.back()); } // --------------------------------------------------------------------------- void WKTFormatter::stopInversion() { assert(!d->inversionStack_.empty()); d->inversionStack_.pop_back(); } // --------------------------------------------------------------------------- bool WKTFormatter::isInverted() const { return d->inversionStack_.back(); } #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static WKTNodeNNPtr null_node(NN_NO_CHECK(internal::make_unique(std::string()))); static inline bool isNull(const WKTNodeNNPtr &node) { return &node == &null_node; } struct WKTNode::Private { std::string value_{}; std::vector children_{}; explicit Private(const std::string &valueIn) : value_(valueIn) {} // cppcheck-suppress functionStatic inline const std::string &value() PROJ_PURE_DEFN { return value_; } // cppcheck-suppress functionStatic inline const std::vector &children() PROJ_PURE_DEFN { return children_; } // cppcheck-suppress functionStatic inline size_t childrenSize() PROJ_PURE_DEFN { return children_.size(); } // cppcheck-suppress functionStatic const WKTNodeNNPtr &lookForChild(const std::string &childName, int occurrence) const noexcept; // cppcheck-suppress functionStatic const WKTNodeNNPtr &lookForChild(const std::string &name) const noexcept; // cppcheck-suppress functionStatic const WKTNodeNNPtr &lookForChild(const std::string &name, const std::string &name2) const noexcept; // cppcheck-suppress functionStatic const WKTNodeNNPtr &lookForChild(const std::string &name, const std::string &name2, const std::string &name3) const noexcept; // cppcheck-suppress functionStatic const WKTNodeNNPtr &lookForChild(const std::string &name, const std::string &name2, const std::string &name3, const std::string &name4) const noexcept; }; #define GP() getPrivate() // --------------------------------------------------------------------------- const WKTNodeNNPtr &WKTNode::Private::lookForChild(const std::string &childName, int occurrence) const noexcept { int occCount = 0; for (const auto &child : children_) { if (ci_equal(child->GP()->value(), childName)) { if (occurrence == occCount) { return child; } occCount++; } } return null_node; } const WKTNodeNNPtr & WKTNode::Private::lookForChild(const std::string &name) const noexcept { for (const auto &child : children_) { const auto &v = child->GP()->value(); if (ci_equal(v, name)) { return child; } } return null_node; } const WKTNodeNNPtr & WKTNode::Private::lookForChild(const std::string &name, const std::string &name2) const noexcept { for (const auto &child : children_) { const auto &v = child->GP()->value(); if (ci_equal(v, name) || ci_equal(v, name2)) { return child; } } return null_node; } const WKTNodeNNPtr & WKTNode::Private::lookForChild(const std::string &name, const std::string &name2, const std::string &name3) const noexcept { for (const auto &child : children_) { const auto &v = child->GP()->value(); if (ci_equal(v, name) || ci_equal(v, name2) || ci_equal(v, name3)) { return child; } } return null_node; } const WKTNodeNNPtr &WKTNode::Private::lookForChild( const std::string &name, const std::string &name2, const std::string &name3, const std::string &name4) const noexcept { for (const auto &child : children_) { const auto &v = child->GP()->value(); if (ci_equal(v, name) || ci_equal(v, name2) || ci_equal(v, name3) || ci_equal(v, name4)) { return child; } } return null_node; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a WKTNode. * * @param valueIn the name of the node. */ WKTNode::WKTNode(const std::string &valueIn) : d(internal::make_unique(valueIn)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress WKTNode::~WKTNode() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Adds a child to the current node. * * @param child child to add. This should not be a parent of this node. */ void WKTNode::addChild(WKTNodeNNPtr &&child) { d->children_.push_back(std::move(child)); } // --------------------------------------------------------------------------- /** \brief Return the (occurrence-1)th sub-node of name childName. * * @param childName name of the child. * @param occurrence occurrence index (starting at 0) * @return the child, or nullptr. */ const WKTNodePtr &WKTNode::lookForChild(const std::string &childName, int occurrence) const noexcept { int occCount = 0; for (const auto &child : d->children_) { if (ci_equal(child->GP()->value(), childName)) { if (occurrence == occCount) { return child; } occCount++; } } return null_node; } // --------------------------------------------------------------------------- /** \brief Return the count of children of given name. * * @param childName name of the children to look for. * @return count */ int WKTNode::countChildrenOfName(const std::string &childName) const noexcept { int occCount = 0; for (const auto &child : d->children_) { if (ci_equal(child->GP()->value(), childName)) { occCount++; } } return occCount; } // --------------------------------------------------------------------------- /** \brief Return the value of a node. */ const std::string &WKTNode::value() const { return d->value_; } // --------------------------------------------------------------------------- /** \brief Return the children of a node. */ const std::vector &WKTNode::children() const { return d->children_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static size_t skipSpace(const std::string &str, size_t start) { size_t i = start; while (i < str.size() && ::isspace(static_cast(str[i]))) { ++i; } return i; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // As used in examples of OGC 12-063r5 static const std::string startPrintedQuote("\xE2\x80\x9C"); static const std::string endPrintedQuote("\xE2\x80\x9D"); //! @endcond WKTNodeNNPtr WKTNode::createFrom(const std::string &wkt, size_t indexStart, int recLevel, size_t &indexEnd) { if (recLevel == 16) { throw ParsingException("too many nesting levels"); } std::string value; size_t i = skipSpace(wkt, indexStart); if (i == wkt.size()) { throw ParsingException("whitespace only string"); } std::string closingStringMarker; bool inString = false; for (; i < wkt.size() && (inString || (wkt[i] != '[' && wkt[i] != '(' && wkt[i] != ',' && wkt[i] != ']' && wkt[i] != ')' && !::isspace(static_cast(wkt[i])))); ++i) { if (wkt[i] == '"') { if (!inString) { inString = true; closingStringMarker = "\""; } else if (closingStringMarker == "\"") { if (i + 1 < wkt.size() && wkt[i + 1] == '"') { i++; } else { inString = false; closingStringMarker.clear(); } } } else if (i + 3 <= wkt.size() && wkt.substr(i, 3) == startPrintedQuote) { if (!inString) { inString = true; closingStringMarker = endPrintedQuote; value += '"'; i += 2; continue; } } else if (i + 3 <= wkt.size() && closingStringMarker == endPrintedQuote && wkt.substr(i, 3) == endPrintedQuote) { inString = false; closingStringMarker.clear(); value += '"'; i += 2; continue; } value += wkt[i]; } i = skipSpace(wkt, i); if (i == wkt.size()) { if (indexStart == 0) { throw ParsingException("missing ["); } else { throw ParsingException("missing , or ]"); } } auto node = NN_NO_CHECK(internal::make_unique(value)); if (indexStart > 0) { if (wkt[i] == ',') { indexEnd = i + 1; return node; } if (wkt[i] == ']' || wkt[i] == ')') { indexEnd = i; return node; } } if (wkt[i] != '[' && wkt[i] != '(') { throw ParsingException("missing ["); } ++i; // skip [ i = skipSpace(wkt, i); while (i < wkt.size() && wkt[i] != ']' && wkt[i] != ')') { size_t indexEndChild; node->addChild(createFrom(wkt, i, recLevel + 1, indexEndChild)); assert(indexEndChild > i); i = indexEndChild; i = skipSpace(wkt, i); if (i < wkt.size() && wkt[i] == ',') { ++i; i = skipSpace(wkt, i); } } if (i == wkt.size() || (wkt[i] != ']' && wkt[i] != ')')) { throw ParsingException("missing ]"); } indexEnd = i + 1; return node; } // --------------------------------------------------------------------------- /** \brief Instantiate a WKTNode hierarchy from a WKT string. * * @param wkt the WKT string to parse. * @param indexStart the start index in the wkt string. * @throw ParsingException */ WKTNodeNNPtr WKTNode::createFrom(const std::string &wkt, size_t indexStart) { size_t indexEnd; return createFrom(wkt, indexStart, 0, indexEnd); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static std::string escapeIfQuotedString(const std::string &str) { if (str.size() > 2 && str[0] == '"' && str.back() == '"') { std::string res("\""); res += replaceAll(str.substr(1, str.size() - 2), "\"", "\"\""); res += '"'; return res; } else { return str; } } //! @endcond // --------------------------------------------------------------------------- /** \brief Return a WKT representation of the tree structure. */ std::string WKTNode::toString() const { std::string str(escapeIfQuotedString(d->value_)); if (!d->children_.empty()) { str += "["; bool first = true; for (auto &child : d->children_) { if (!first) { str += ','; } first = false; str += child->toString(); } str += "]"; } return str; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct WKTParser::Private { bool strict_ = true; std::list warningList_{}; std::vector toWGS84Parameters_{}; std::string datumPROJ4Grids_{}; bool esriStyle_ = false; DatabaseContextPtr dbContext_{}; static constexpr int MAX_PROPERTY_SIZE = 1024; PropertyMap **properties_{}; int propertyCount_ = 0; Private() { properties_ = new PropertyMap *[MAX_PROPERTY_SIZE]; } ~Private() { for (int i = 0; i < propertyCount_; i++) { delete properties_[i]; } delete[] properties_; } Private(const Private &) = delete; Private &operator=(const Private &) = delete; void emitRecoverableWarning(const std::string &errorMsg); BaseObjectNNPtr build(const WKTNodeNNPtr &node); IdentifierPtr buildId(const WKTNodeNNPtr &node, bool tolerant, bool removeInverseOf); PropertyMap &buildProperties(const WKTNodeNNPtr &node, bool removeInverseOf = false); ObjectDomainPtr buildObjectDomain(const WKTNodeNNPtr &node); static std::string stripQuotes(const WKTNodeNNPtr &node); static double asDouble(const WKTNodeNNPtr &node); UnitOfMeasure buildUnit(const WKTNodeNNPtr &node, UnitOfMeasure::Type type = UnitOfMeasure::Type::UNKNOWN); UnitOfMeasure buildUnitInSubNode( const WKTNodeNNPtr &node, common::UnitOfMeasure::Type type = UnitOfMeasure::Type::UNKNOWN); EllipsoidNNPtr buildEllipsoid(const WKTNodeNNPtr &node); PrimeMeridianNNPtr buildPrimeMeridian(const WKTNodeNNPtr &node, const UnitOfMeasure &defaultAngularUnit); static optional getAnchor(const WKTNodeNNPtr &node); static void parseDynamic(const WKTNodeNNPtr &dynamicNode, double &frameReferenceEpoch, util::optional &modelName); GeodeticReferenceFrameNNPtr buildGeodeticReferenceFrame(const WKTNodeNNPtr &node, const PrimeMeridianNNPtr &primeMeridian, const WKTNodeNNPtr &dynamicNode); DatumEnsembleNNPtr buildDatumEnsemble(const WKTNodeNNPtr &node, const PrimeMeridianPtr &primeMeridian, bool expectEllipsoid); MeridianNNPtr buildMeridian(const WKTNodeNNPtr &node); CoordinateSystemAxisNNPtr buildAxis(const WKTNodeNNPtr &node, const UnitOfMeasure &unitIn, const UnitOfMeasure::Type &unitType, bool isGeocentric, int expectedOrderNum); CoordinateSystemNNPtr buildCS(const WKTNodeNNPtr &node, /* maybe null */ const WKTNodeNNPtr &parentNode, const UnitOfMeasure &defaultAngularUnit); GeodeticCRSNNPtr buildGeodeticCRS(const WKTNodeNNPtr &node); CRSNNPtr buildDerivedGeodeticCRS(const WKTNodeNNPtr &node); static UnitOfMeasure guessUnitForParameter(const std::string ¶mName, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); void consumeParameters(const WKTNodeNNPtr &node, bool isAbridged, std::vector ¶meters, std::vector &values, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); static void addExtensionProj4ToProp(const WKTNode::Private *nodeP, PropertyMap &props); ConversionNNPtr buildConversion(const WKTNodeNNPtr &node, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); static bool hasWebMercPROJ4String(const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode); static std::string projectionGetParameter(const WKTNodeNNPtr &projCRSNode, const char *paramName); ConversionNNPtr buildProjection(const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); ConversionNNPtr buildProjectionStandard(const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); ConversionNNPtr buildProjectionFromESRI(const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit); ProjectedCRSNNPtr buildProjectedCRS(const WKTNodeNNPtr &node); VerticalReferenceFrameNNPtr buildVerticalReferenceFrame(const WKTNodeNNPtr &node, const WKTNodeNNPtr &dynamicNode); TemporalDatumNNPtr buildTemporalDatum(const WKTNodeNNPtr &node); EngineeringDatumNNPtr buildEngineeringDatum(const WKTNodeNNPtr &node); ParametricDatumNNPtr buildParametricDatum(const WKTNodeNNPtr &node); CRSNNPtr buildVerticalCRS(const WKTNodeNNPtr &node); DerivedVerticalCRSNNPtr buildDerivedVerticalCRS(const WKTNodeNNPtr &node); CompoundCRSNNPtr buildCompoundCRS(const WKTNodeNNPtr &node); BoundCRSNNPtr buildBoundCRS(const WKTNodeNNPtr &node); TemporalCSNNPtr buildTemporalCS(const WKTNodeNNPtr &parentNode); TemporalCRSNNPtr buildTemporalCRS(const WKTNodeNNPtr &node); DerivedTemporalCRSNNPtr buildDerivedTemporalCRS(const WKTNodeNNPtr &node); EngineeringCRSNNPtr buildEngineeringCRS(const WKTNodeNNPtr &node); EngineeringCRSNNPtr buildEngineeringCRSFromLocalCS(const WKTNodeNNPtr &node); DerivedEngineeringCRSNNPtr buildDerivedEngineeringCRS(const WKTNodeNNPtr &node); ParametricCSNNPtr buildParametricCS(const WKTNodeNNPtr &parentNode); ParametricCRSNNPtr buildParametricCRS(const WKTNodeNNPtr &node); DerivedParametricCRSNNPtr buildDerivedParametricCRS(const WKTNodeNNPtr &node); DerivedProjectedCRSNNPtr buildDerivedProjectedCRS(const WKTNodeNNPtr &node); CRSPtr buildCRS(const WKTNodeNNPtr &node); TransformationNNPtr buildCoordinateOperation(const WKTNodeNNPtr &node); ConcatenatedOperationNNPtr buildConcatenatedOperation(const WKTNodeNNPtr &node); }; // --------------------------------------------------------------------------- WKTParser::WKTParser() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress WKTParser::~WKTParser() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Set whether parsing should be done in strict mode. */ WKTParser &WKTParser::setStrict(bool strict) { d->strict_ = strict; return *this; } // --------------------------------------------------------------------------- /** \brief Return the list of warnings found during parsing. * * \note The list might be non-empty only is setStrict(false) has been called. */ std::list WKTParser::warningList() const { return d->warningList_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void WKTParser::Private::emitRecoverableWarning(const std::string &errorMsg) { if (strict_) { throw ParsingException(errorMsg); } else { warningList_.push_back(errorMsg); } } // --------------------------------------------------------------------------- static double asDouble(const std::string &val) { return c_locale_stod(val); } // --------------------------------------------------------------------------- PROJ_NO_RETURN static void ThrowNotEnoughChildren(const std::string &nodeName) { throw ParsingException( concat("not enough children in ", nodeName, " node")); } // --------------------------------------------------------------------------- PROJ_NO_RETURN static void ThrowNotRequiredNumberOfChildren(const std::string &nodeName) { throw ParsingException( concat("not required number of children in ", nodeName, " node")); } // --------------------------------------------------------------------------- PROJ_NO_RETURN static void ThrowMissing(const std::string &nodeName) { throw ParsingException(concat("missing ", nodeName, " node")); } // --------------------------------------------------------------------------- PROJ_NO_RETURN static void ThrowNotExpectedCSType(const std::string &expectedCSType) { throw ParsingException(concat("CS node is not of type ", expectedCSType)); } // --------------------------------------------------------------------------- static ParsingException buildRethrow(const char *funcName, const std::exception &e) { std::string res(funcName); res += ": "; res += e.what(); return ParsingException(res); } // --------------------------------------------------------------------------- std::string WKTParser::Private::stripQuotes(const WKTNodeNNPtr &node) { return ::stripQuotes(node->GP()->value()); } // --------------------------------------------------------------------------- double WKTParser::Private::asDouble(const WKTNodeNNPtr &node) { return io::asDouble(node->GP()->value()); } // --------------------------------------------------------------------------- IdentifierPtr WKTParser::Private::buildId(const WKTNodeNNPtr &node, bool tolerant, bool removeInverseOf) { const auto *nodeP = node->GP(); const auto &nodeChidren = nodeP->children(); if (nodeChidren.size() >= 2) { auto codeSpace = stripQuotes(nodeChidren[0]); if (removeInverseOf && starts_with(codeSpace, "INVERSE(") && codeSpace.back() == ')') { codeSpace = codeSpace.substr(strlen("INVERSE(")); codeSpace.resize(codeSpace.size() - 1); } auto code = stripQuotes(nodeChidren[1]); auto &citationNode = nodeP->lookForChild(WKTConstants::CITATION); auto &uriNode = nodeP->lookForChild(WKTConstants::URI); PropertyMap propertiesId; propertiesId.set(Identifier::CODESPACE_KEY, codeSpace); bool authoritySet = false; /*if (!isNull(citationNode))*/ { const auto *citationNodeP = citationNode->GP(); if (citationNodeP->childrenSize() == 1) { authoritySet = true; propertiesId.set(Identifier::AUTHORITY_KEY, stripQuotes(citationNodeP->children()[0])); } } if (!authoritySet) { propertiesId.set(Identifier::AUTHORITY_KEY, codeSpace); } /*if (!isNull(uriNode))*/ { const auto *uriNodeP = uriNode->GP(); if (uriNodeP->childrenSize() == 1) { propertiesId.set(Identifier::URI_KEY, stripQuotes(uriNodeP->children()[0])); } } if (nodeChidren.size() >= 3 && nodeChidren[2]->GP()->childrenSize() == 0) { auto version = stripQuotes(nodeChidren[2]); propertiesId.set(Identifier::VERSION_KEY, version); } return Identifier::create(code, propertiesId); } else if (strict_ || !tolerant) { ThrowNotEnoughChildren(nodeP->value()); } else { std::string msg("not enough children in "); msg += nodeP->value(); msg += " node"; warningList_.emplace_back(std::move(msg)); } return nullptr; } // --------------------------------------------------------------------------- PropertyMap &WKTParser::Private::buildProperties(const WKTNodeNNPtr &node, bool removeInverseOf) { if (propertyCount_ == MAX_PROPERTY_SIZE) { throw ParsingException("MAX_PROPERTY_SIZE reached"); } properties_[propertyCount_] = new PropertyMap(); auto &&properties = properties_[propertyCount_]; propertyCount_++; std::string authNameFromAlias; std::string codeFromAlias; const auto *nodeP = node->GP(); const auto &nodeChildren = nodeP->children(); if (!nodeChildren.empty()) { const auto &nodeName(nodeP->value()); auto name(stripQuotes(nodeChildren[0])); if (removeInverseOf && starts_with(name, "Inverse of ")) { name = name.substr(strlen("Inverse of ")); } if (ends_with(name, " (deprecated)")) { name.resize(name.size() - strlen(" (deprecated)")); properties->set(common::IdentifiedObject::DEPRECATED_KEY, true); } const char *tableNameForAlias = nullptr; if (ci_equal(nodeName, WKTConstants::GEOGCS)) { if (starts_with(name, "GCS_")) { esriStyle_ = true; if (name == "GCS_WGS_1984") { name = "WGS 84"; } else { tableNameForAlias = "geodetic_crs"; } } } else if (esriStyle_ && ci_equal(nodeName, WKTConstants::SPHEROID)) { if (name == "WGS_1984") { name = "WGS 84"; authNameFromAlias = Identifier::EPSG; codeFromAlias = "7030"; } else { tableNameForAlias = "ellipsoid"; } } if (dbContext_ && tableNameForAlias) { std::string outTableName; auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto officialName = authFactory->getOfficialNameFromAlias( name, tableNameForAlias, "ESRI", false, outTableName, authNameFromAlias, codeFromAlias); if (!officialName.empty()) { name = officialName; // Clearing authority for geodetic_crs because of // potential axis order mismatch. if (strcmp(tableNameForAlias, "geodetic_crs") == 0) { authNameFromAlias.clear(); codeFromAlias.clear(); } } } properties->set(IdentifiedObject::NAME_KEY, name); } auto identifiers = ArrayOfBaseObject::create(); for (const auto &subNode : nodeChildren) { const auto &subNodeName(subNode->GP()->value()); if (ci_equal(subNodeName, WKTConstants::ID) || ci_equal(subNodeName, WKTConstants::AUTHORITY)) { auto id = buildId(subNode, true, removeInverseOf); if (id) { identifiers->add(NN_NO_CHECK(id)); } } } if (identifiers->empty() && !authNameFromAlias.empty()) { identifiers->add(Identifier::create( codeFromAlias, PropertyMap() .set(Identifier::CODESPACE_KEY, authNameFromAlias) .set(Identifier::AUTHORITY_KEY, authNameFromAlias))); } if (!identifiers->empty()) { properties->set(IdentifiedObject::IDENTIFIERS_KEY, identifiers); } auto &remarkNode = nodeP->lookForChild(WKTConstants::REMARK); if (!isNull(remarkNode)) { const auto &remarkChildren = remarkNode->GP()->children(); if (remarkChildren.size() == 1) { properties->set(IdentifiedObject::REMARKS_KEY, stripQuotes(remarkChildren[0])); } else { ThrowNotRequiredNumberOfChildren(remarkNode->GP()->value()); } } ArrayOfBaseObjectNNPtr array = ArrayOfBaseObject::create(); for (const auto &subNode : nodeP->children()) { const auto &subNodeName(subNode->GP()->value()); if (ci_equal(subNodeName, WKTConstants::USAGE)) { auto objectDomain = buildObjectDomain(subNode); if (!objectDomain) { throw ParsingException( concat("missing children in ", subNodeName, " node")); } array->add(NN_NO_CHECK(objectDomain)); } } if (!array->empty()) { properties->set(ObjectUsage::OBJECT_DOMAIN_KEY, array); } else { auto objectDomain = buildObjectDomain(node); if (objectDomain) { properties->set(ObjectUsage::OBJECT_DOMAIN_KEY, NN_NO_CHECK(objectDomain)); } } auto &versionNode = nodeP->lookForChild(WKTConstants::VERSION); if (!isNull(versionNode)) { const auto &versionChildren = versionNode->GP()->children(); if (versionChildren.size() == 1) { properties->set(CoordinateOperation::OPERATION_VERSION_KEY, stripQuotes(versionChildren[0])); } else { ThrowNotRequiredNumberOfChildren(versionNode->GP()->value()); } } return *properties; } // --------------------------------------------------------------------------- ObjectDomainPtr WKTParser::Private::buildObjectDomain(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &scopeNode = nodeP->lookForChild(WKTConstants::SCOPE); auto &areaNode = nodeP->lookForChild(WKTConstants::AREA); auto &bboxNode = nodeP->lookForChild(WKTConstants::BBOX); auto &verticalExtentNode = nodeP->lookForChild(WKTConstants::VERTICALEXTENT); auto &temporalExtentNode = nodeP->lookForChild(WKTConstants::TIMEEXTENT); if (!isNull(scopeNode) || !isNull(areaNode) || !isNull(bboxNode) || !isNull(verticalExtentNode) || !isNull(temporalExtentNode)) { optional scope; const auto *scopeNodeP = scopeNode->GP(); const auto &scopeChildren = scopeNodeP->children(); if (scopeChildren.size() == 1) { scope = stripQuotes(scopeChildren[0]); } ExtentPtr extent; if (!isNull(areaNode) || !isNull(bboxNode)) { util::optional description; std::vector geogExtent; std::vector verticalExtent; std::vector temporalExtent; if (!isNull(areaNode)) { const auto &areaChildren = areaNode->GP()->children(); if (areaChildren.size() == 1) { description = stripQuotes(areaChildren[0]); } else { ThrowNotRequiredNumberOfChildren(areaNode->GP()->value()); } } if (!isNull(bboxNode)) { const auto &bboxChildren = bboxNode->GP()->children(); if (bboxChildren.size() == 4) { try { double south = asDouble(bboxChildren[0]); double west = asDouble(bboxChildren[1]); double north = asDouble(bboxChildren[2]); double east = asDouble(bboxChildren[3]); auto bbox = GeographicBoundingBox::create(west, south, east, north); geogExtent.emplace_back(bbox); } catch (const std::exception &) { throw ParsingException(concat("not 4 double values in ", bboxNode->GP()->value(), " node")); } } else { ThrowNotRequiredNumberOfChildren(bboxNode->GP()->value()); } } if (!isNull(verticalExtentNode)) { const auto &verticalExtentChildren = verticalExtentNode->GP()->children(); const auto verticalExtentChildrenSize = verticalExtentChildren.size(); if (verticalExtentChildrenSize == 2 || verticalExtentChildrenSize == 3) { double min; double max; try { min = asDouble(verticalExtentChildren[0]); max = asDouble(verticalExtentChildren[1]); } catch (const std::exception &) { throw ParsingException( concat("not 2 double values in ", verticalExtentNode->GP()->value(), " node")); } UnitOfMeasure unit = UnitOfMeasure::METRE; if (verticalExtentChildrenSize == 3) { unit = buildUnit(verticalExtentChildren[2], UnitOfMeasure::Type::LINEAR); } verticalExtent.emplace_back(VerticalExtent::create( min, max, util::nn_make_shared(unit))); } else { ThrowNotRequiredNumberOfChildren( verticalExtentNode->GP()->value()); } } if (!isNull(temporalExtentNode)) { const auto &temporalExtentChildren = temporalExtentNode->GP()->children(); if (temporalExtentChildren.size() == 2) { temporalExtent.emplace_back(TemporalExtent::create( stripQuotes(temporalExtentChildren[0]), stripQuotes(temporalExtentChildren[1]))); } else { ThrowNotRequiredNumberOfChildren( temporalExtentNode->GP()->value()); } } extent = Extent::create(description, geogExtent, verticalExtent, temporalExtent) .as_nullable(); } return ObjectDomain::create(scope, extent).as_nullable(); } return nullptr; } // --------------------------------------------------------------------------- UnitOfMeasure WKTParser::Private::buildUnit(const WKTNodeNNPtr &node, UnitOfMeasure::Type type) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if ((type != UnitOfMeasure::Type::TIME && children.size() < 2) || (type == UnitOfMeasure::Type::TIME && children.size() < 1)) { ThrowNotEnoughChildren(nodeP->value()); } try { std::string unitName(stripQuotes(children[0])); PropertyMap properties(buildProperties(node)); auto &idNode = nodeP->lookForChild(WKTConstants::ID, WKTConstants::AUTHORITY); if (!isNull(idNode) && idNode->GP()->childrenSize() < 2) { emitRecoverableWarning("not enough children in " + idNode->GP()->value() + " node"); } const bool hasValidIdNode = !isNull(idNode) && idNode->GP()->childrenSize() >= 2; const auto &idNodeChildren(idNode->GP()->children()); std::string codeSpace(hasValidIdNode ? stripQuotes(idNodeChildren[0]) : std::string()); std::string code(hasValidIdNode ? stripQuotes(idNodeChildren[1]) : std::string()); bool queryDb = true; if (type == UnitOfMeasure::Type::UNKNOWN) { if (ci_equal(unitName, "METER") || ci_equal(unitName, "METRE")) { type = UnitOfMeasure::Type::LINEAR; unitName = "metre"; if (codeSpace.empty()) { codeSpace = Identifier::EPSG; code = "9001"; queryDb = false; } } else if (ci_equal(unitName, "DEGREE") || ci_equal(unitName, "GRAD")) { type = UnitOfMeasure::Type::ANGULAR; } } if (esriStyle_ && dbContext_ && queryDb) { std::string outTableName; std::string authNameFromAlias; std::string codeFromAlias; auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto officialName = authFactory->getOfficialNameFromAlias( unitName, "unit_of_measure", "ESRI", false, outTableName, authNameFromAlias, codeFromAlias); if (!officialName.empty()) { unitName = officialName; codeSpace = authNameFromAlias; code = codeFromAlias; } } double convFactor = children.size() >= 2 ? asDouble(children[1]) : 0.0; constexpr double US_FOOT_CONV_FACTOR = 12.0 / 39.37; constexpr double REL_ERROR = 1e-10; // Fix common rounding errors if (std::fabs(convFactor - UnitOfMeasure::DEGREE.conversionToSI()) < REL_ERROR * convFactor) { convFactor = UnitOfMeasure::DEGREE.conversionToSI(); } else if (std::fabs(convFactor - US_FOOT_CONV_FACTOR) < REL_ERROR * convFactor) { convFactor = US_FOOT_CONV_FACTOR; } return UnitOfMeasure(unitName, convFactor, type, codeSpace, code); } catch (const std::exception &e) { throw buildRethrow(__FUNCTION__, e); } } // --------------------------------------------------------------------------- // node here is a parent node, not a UNIT/LENGTHUNIT/ANGLEUNIT/TIMEUNIT/... node UnitOfMeasure WKTParser::Private::buildUnitInSubNode(const WKTNodeNNPtr &node, UnitOfMeasure::Type type) { const auto *nodeP = node->GP(); { auto &unitNode = nodeP->lookForChild(WKTConstants::LENGTHUNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::LINEAR); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::ANGLEUNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::ANGULAR); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::SCALEUNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::SCALE); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::TIMEUNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::TIME); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::TEMPORALQUANTITY); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::TIME); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::PARAMETRICUNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, UnitOfMeasure::Type::PARAMETRIC); } } { auto &unitNode = nodeP->lookForChild(WKTConstants::UNIT); if (!isNull(unitNode)) { return buildUnit(unitNode, type); } } return UnitOfMeasure::NONE; } // --------------------------------------------------------------------------- EllipsoidNNPtr WKTParser::Private::buildEllipsoid(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if (children.size() < 3) { ThrowNotEnoughChildren(nodeP->value()); } try { UnitOfMeasure unit = buildUnitInSubNode(node, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { unit = UnitOfMeasure::METRE; } Length semiMajorAxis(asDouble(children[1]), unit); Scale invFlattening(asDouble(children[2])); const auto celestialBody( Ellipsoid::guessBodyName(dbContext_, semiMajorAxis.getSIValue())); if (invFlattening.getSIValue() == 0) { return Ellipsoid::createSphere(buildProperties(node), semiMajorAxis, celestialBody); } else { return Ellipsoid::createFlattenedSphere( buildProperties(node), semiMajorAxis, invFlattening, celestialBody); } } catch (const std::exception &e) { throw buildRethrow(__FUNCTION__, e); } } // --------------------------------------------------------------------------- PrimeMeridianNNPtr WKTParser::Private::buildPrimeMeridian( const WKTNodeNNPtr &node, const UnitOfMeasure &defaultAngularUnit) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if (children.size() < 2) { ThrowNotEnoughChildren(nodeP->value()); } auto name = stripQuotes(children[0]); UnitOfMeasure unit = buildUnitInSubNode(node, UnitOfMeasure::Type::ANGULAR); if (unit == UnitOfMeasure::NONE) { unit = defaultAngularUnit; if (unit == UnitOfMeasure::NONE) { unit = UnitOfMeasure::DEGREE; } } try { double angleValue = asDouble(children[1]); // Correct for GDAL WKT1 departure if (name == "Paris" && std::fabs(angleValue - 2.33722917) < 1e-8 && unit == UnitOfMeasure::GRAD) { angleValue = 2.5969213; } Angle angle(angleValue, unit); return PrimeMeridian::create(buildProperties(node), angle); } catch (const std::exception &e) { throw buildRethrow(__FUNCTION__, e); } } // --------------------------------------------------------------------------- optional WKTParser::Private::getAnchor(const WKTNodeNNPtr &node) { auto &anchorNode = node->GP()->lookForChild(WKTConstants::ANCHOR); if (anchorNode->GP()->childrenSize() == 1) { return optional( stripQuotes(anchorNode->GP()->children()[0])); } return optional(); } // --------------------------------------------------------------------------- static const PrimeMeridianNNPtr & fixupPrimeMeridan(const EllipsoidNNPtr &ellipsoid, const PrimeMeridianNNPtr &pm) { return (ellipsoid->celestialBody() != Ellipsoid::EARTH && pm.get() == PrimeMeridian::GREENWICH.get()) ? PrimeMeridian::REFERENCE_MERIDIAN : pm; } // --------------------------------------------------------------------------- GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame( const WKTNodeNNPtr &node, const PrimeMeridianNNPtr &primeMeridian, const WKTNodeNNPtr &dynamicNode) { const auto *nodeP = node->GP(); auto &ellipsoidNode = nodeP->lookForChild(WKTConstants::ELLIPSOID, WKTConstants::SPHEROID); if (isNull(ellipsoidNode)) { ThrowMissing(WKTConstants::ELLIPSOID); } auto &properties = buildProperties(node); // do that before buildEllipsoid() so that esriStyle_ can be set auto name = stripQuotes(nodeP->children()[0]); const auto identifyFromName = [&](const std::string &l_name) { bool foundDatumName = false; if (dbContext_) { auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto res = authFactory->createObjectsFromName( l_name, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true, 1); if (!res.empty()) { const auto &refDatum = res.front(); if (metadata::Identifier::isEquivalentName( l_name.c_str(), refDatum->nameStr().c_str())) { foundDatumName = true; properties.set(IdentifiedObject::NAME_KEY, refDatum->nameStr()); if (!properties.get(Identifier::CODESPACE_KEY) && refDatum->identifiers().size() == 1) { const auto &id = refDatum->identifiers()[0]; auto identifiers = ArrayOfBaseObject::create(); identifiers->add(Identifier::create( id->code(), PropertyMap() .set(Identifier::CODESPACE_KEY, *id->codeSpace()) .set(Identifier::AUTHORITY_KEY, *id->codeSpace()))); properties.set(IdentifiedObject::IDENTIFIERS_KEY, identifiers); } } } else { // Get official name from database if AUTHORITY is present auto &idNode = nodeP->lookForChild(WKTConstants::AUTHORITY); if (!isNull(idNode)) { try { auto id = buildId(idNode, true, false); auto authFactory2 = AuthorityFactory::create( NN_NO_CHECK(dbContext_), *id->codeSpace()); auto dbDatum = authFactory2->createGeodeticDatum(id->code()); foundDatumName = true; properties.set(IdentifiedObject::NAME_KEY, dbDatum->nameStr()); } catch (const std::exception &) { } } } if (!foundDatumName) { std::string outTableName; std::string authNameFromAlias; std::string codeFromAlias; auto officialName = authFactory->getOfficialNameFromAlias( l_name, "geodetic_datum", std::string(), true, outTableName, authNameFromAlias, codeFromAlias); if (!officialName.empty()) { foundDatumName = true; properties.set(IdentifiedObject::NAME_KEY, officialName); } } } return foundDatumName; }; if (name == "WGS_1984") { properties.set(IdentifiedObject::NAME_KEY, GeodeticReferenceFrame::EPSG_6326->nameStr()); } else if (starts_with(name, "D_")) { esriStyle_ = true; const char *tableNameForAlias = nullptr; std::string authNameFromAlias; std::string codeFromAlias; if (name == "D_WGS_1984") { name = "World Geodetic System 1984"; authNameFromAlias = Identifier::EPSG; codeFromAlias = "6326"; } else { tableNameForAlias = "geodetic_datum"; } bool setNameAndId = true; if (dbContext_ && tableNameForAlias) { std::string outTableName; auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto officialName = authFactory->getOfficialNameFromAlias( name, tableNameForAlias, "ESRI", false, outTableName, authNameFromAlias, codeFromAlias); if (officialName.empty()) { // For the case of "D_GDA2020" where there is no D_GDA2020 ESRI // alias, so just try without the D_ prefix. const auto nameWithoutDPrefix = name.substr(2); if (identifyFromName(nameWithoutDPrefix)) { setNameAndId = false; // already done in identifyFromName() } } else { if (primeMeridian->nameStr() != PrimeMeridian::GREENWICH->nameStr()) { auto nameWithPM = officialName + " (" + primeMeridian->nameStr() + ")"; if (dbContext_->isKnownName(nameWithPM, "geodetic_datum")) { officialName = nameWithPM; } } name = officialName; } } if (setNameAndId) { properties.set(IdentifiedObject::NAME_KEY, name); if (!authNameFromAlias.empty()) { auto identifiers = ArrayOfBaseObject::create(); identifiers->add(Identifier::create( codeFromAlias, PropertyMap() .set(Identifier::CODESPACE_KEY, authNameFromAlias) .set(Identifier::AUTHORITY_KEY, authNameFromAlias))); properties.set(IdentifiedObject::IDENTIFIERS_KEY, identifiers); } } } else if (name.find('_') != std::string::npos) { // Likely coming from WKT1 identifyFromName(name); } auto ellipsoid = buildEllipsoid(ellipsoidNode); const auto &primeMeridianModified = fixupPrimeMeridan(ellipsoid, primeMeridian); auto &TOWGS84Node = nodeP->lookForChild(WKTConstants::TOWGS84); if (!isNull(TOWGS84Node)) { const auto &TOWGS84Children = TOWGS84Node->GP()->children(); const size_t TOWGS84Size = TOWGS84Children.size(); if (TOWGS84Size == 3 || TOWGS84Size == 7) { try { for (const auto &child : TOWGS84Children) { toWGS84Parameters_.push_back(asDouble(child)); } for (size_t i = TOWGS84Size; i < 7; ++i) { toWGS84Parameters_.push_back(0.0); } } catch (const std::exception &) { throw ParsingException("Invalid TOWGS84 node"); } } else { throw ParsingException("Invalid TOWGS84 node"); } } auto &extensionNode = nodeP->lookForChild(WKTConstants::EXTENSION); const auto &extensionChildren = extensionNode->GP()->children(); if (extensionChildren.size() == 2) { if (ci_equal(stripQuotes(extensionChildren[0]), "PROJ4_GRIDS")) { datumPROJ4Grids_ = stripQuotes(extensionChildren[1]); } } if (!isNull(dynamicNode)) { double frameReferenceEpoch = 0.0; util::optional modelName; parseDynamic(dynamicNode, frameReferenceEpoch, modelName); return DynamicGeodeticReferenceFrame::create( properties, ellipsoid, getAnchor(node), primeMeridianModified, common::Measure(frameReferenceEpoch, common::UnitOfMeasure::YEAR), modelName); } return GeodeticReferenceFrame::create( properties, ellipsoid, getAnchor(node), primeMeridianModified); } // --------------------------------------------------------------------------- DatumEnsembleNNPtr WKTParser::Private::buildDatumEnsemble(const WKTNodeNNPtr &node, const PrimeMeridianPtr &primeMeridian, bool expectEllipsoid) { const auto *nodeP = node->GP(); auto &ellipsoidNode = nodeP->lookForChild(WKTConstants::ELLIPSOID, WKTConstants::SPHEROID); if (expectEllipsoid && isNull(ellipsoidNode)) { ThrowMissing(WKTConstants::ELLIPSOID); } std::vector datums; for (const auto &subNode : nodeP->children()) { if (ci_equal(subNode->GP()->value(), WKTConstants::MEMBER)) { if (subNode->GP()->childrenSize() == 0) { throw ParsingException("Invalid MEMBER node"); } if (expectEllipsoid) { datums.emplace_back(GeodeticReferenceFrame::create( buildProperties(subNode), buildEllipsoid(ellipsoidNode), optional(), primeMeridian ? NN_NO_CHECK(primeMeridian) : PrimeMeridian::GREENWICH)); } else { datums.emplace_back( VerticalReferenceFrame::create(buildProperties(subNode))); } } } auto &accuracyNode = nodeP->lookForChild(WKTConstants::ENSEMBLEACCURACY); auto &accuracyNodeChildren = accuracyNode->GP()->children(); if (accuracyNodeChildren.empty()) { ThrowMissing(WKTConstants::ENSEMBLEACCURACY); } auto accuracy = PositionalAccuracy::create(accuracyNodeChildren[0]->GP()->value()); try { return DatumEnsemble::create(buildProperties(node), datums, accuracy); } catch (const util::Exception &e) { throw buildRethrow(__FUNCTION__, e); } } // --------------------------------------------------------------------------- MeridianNNPtr WKTParser::Private::buildMeridian(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if (children.size() < 2) { ThrowNotEnoughChildren(nodeP->value()); } UnitOfMeasure unit = buildUnitInSubNode(node, UnitOfMeasure::Type::ANGULAR); try { double angleValue = asDouble(children[0]); Angle angle(angleValue, unit); return Meridian::create(angle); } catch (const std::exception &e) { throw buildRethrow(__FUNCTION__, e); } } // --------------------------------------------------------------------------- PROJ_NO_RETURN static void ThrowParsingExceptionMissingUNIT() { throw ParsingException("buildCS: missing UNIT"); } // --------------------------------------------------------------------------- CoordinateSystemAxisNNPtr WKTParser::Private::buildAxis(const WKTNodeNNPtr &node, const UnitOfMeasure &unitIn, const UnitOfMeasure::Type &unitType, bool isGeocentric, int expectedOrderNum) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if (children.size() < 2) { ThrowNotEnoughChildren(nodeP->value()); } auto &orderNode = nodeP->lookForChild(WKTConstants::ORDER); if (!isNull(orderNode)) { const auto &orderNodeChildren = orderNode->GP()->children(); if (orderNodeChildren.size() != 1) { ThrowNotEnoughChildren(WKTConstants::ORDER); } const auto &order = orderNodeChildren[0]->GP()->value(); int orderNum; try { orderNum = std::stoi(order); } catch (const std::exception &) { throw ParsingException( concat("buildAxis: invalid ORDER value: ", order)); } if (orderNum != expectedOrderNum) { throw ParsingException( concat("buildAxis: did not get expected ORDER value: ", order)); } } // The axis designation in WK2 can be: "name", "(abbrev)" or "name // (abbrev)" std::string axisDesignation(stripQuotes(children[0])); size_t sepPos = axisDesignation.find(" ("); std::string axisName; std::string abbreviation; if (sepPos != std::string::npos && axisDesignation.back() == ')') { axisName = CoordinateSystemAxis::normalizeAxisName( axisDesignation.substr(0, sepPos)); abbreviation = axisDesignation.substr(sepPos + 2); abbreviation.resize(abbreviation.size() - 1); } else if (!axisDesignation.empty() && axisDesignation[0] == '(' && axisDesignation.back() == ')') { abbreviation = axisDesignation.substr(1, axisDesignation.size() - 2); if (abbreviation == AxisAbbreviation::E) { axisName = AxisName::Easting; } else if (abbreviation == AxisAbbreviation::N) { axisName = AxisName::Northing; } else if (abbreviation == AxisAbbreviation::lat) { axisName = AxisName::Latitude; } else if (abbreviation == AxisAbbreviation::lon) { axisName = AxisName::Longitude; } } else { axisName = CoordinateSystemAxis::normalizeAxisName(axisDesignation); if (axisName == AxisName::Latitude) { abbreviation = AxisAbbreviation::lat; } else if (axisName == AxisName::Longitude) { abbreviation = AxisAbbreviation::lon; } else if (axisName == AxisName::Ellipsoidal_height) { abbreviation = AxisAbbreviation::h; } } const std::string &dirString = children[1]->GP()->value(); const AxisDirection *direction = AxisDirection::valueOf(dirString); // WKT2, geocentric CS: axis names are omitted if (axisName.empty()) { if (direction == &AxisDirection::GEOCENTRIC_X && abbreviation == AxisAbbreviation::X) { axisName = AxisName::Geocentric_X; } else if (direction == &AxisDirection::GEOCENTRIC_Y && abbreviation == AxisAbbreviation::Y) { axisName = AxisName::Geocentric_Y; } else if (direction == &AxisDirection::GEOCENTRIC_Z && abbreviation == AxisAbbreviation::Z) { axisName = AxisName::Geocentric_Z; } } // WKT1 if (!direction && isGeocentric && axisName == AxisName::Geocentric_X) { abbreviation = AxisAbbreviation::X; direction = &AxisDirection::GEOCENTRIC_X; } else if (!direction && isGeocentric && axisName == AxisName::Geocentric_Y) { abbreviation = AxisAbbreviation::Y; direction = &AxisDirection::GEOCENTRIC_Y; } else if (isGeocentric && axisName == AxisName::Geocentric_Z && (dirString == AxisDirectionWKT1::NORTH.toString() || dirString == AxisDirectionWKT1::OTHER.toString())) { abbreviation = AxisAbbreviation::Z; direction = &AxisDirection::GEOCENTRIC_Z; } else if (dirString == AxisDirectionWKT1::OTHER.toString()) { direction = &AxisDirection::UNSPECIFIED; } else if (!direction && AxisDirectionWKT1::valueOf(toupper(dirString)) != nullptr) { direction = AxisDirection::valueOf(tolower(dirString)); } if (!direction) { throw ParsingException( concat("unhandled axis direction: ", children[1]->GP()->value())); } UnitOfMeasure unit(buildUnitInSubNode(node)); if (unit == UnitOfMeasure::NONE) { // If no unit in the AXIS node, use the one potentially coming from // the CS. unit = unitIn; if (unit == UnitOfMeasure::NONE && unitType != UnitOfMeasure::Type::NONE && unitType != UnitOfMeasure::Type::TIME) { ThrowParsingExceptionMissingUNIT(); } } auto &meridianNode = nodeP->lookForChild(WKTConstants::MERIDIAN); return CoordinateSystemAxis::create( buildProperties(node).set(IdentifiedObject::NAME_KEY, axisName), abbreviation, *direction, unit, !isNull(meridianNode) ? buildMeridian(meridianNode).as_nullable() : nullptr); } // --------------------------------------------------------------------------- static const PropertyMap emptyPropertyMap{}; PROJ_NO_RETURN static void ThrowParsingException(const std::string &msg) { throw ParsingException(msg); } static ParsingException buildParsingExceptionInvalidAxisCount(const std::string &csType) { return ParsingException( concat("buildCS: invalid CS axis count for ", csType)); } CoordinateSystemNNPtr WKTParser::Private::buildCS(const WKTNodeNNPtr &node, /* maybe null */ const WKTNodeNNPtr &parentNode, const UnitOfMeasure &defaultAngularUnit) { bool isGeocentric = false; std::string csType; const int numberOfAxis = parentNode->countChildrenOfName(WKTConstants::AXIS); int axisCount = numberOfAxis; if (!isNull(node)) { const auto *nodeP = node->GP(); const auto &children = nodeP->children(); if (children.size() < 2) { ThrowNotEnoughChildren(nodeP->value()); } csType = children[0]->GP()->value(); try { axisCount = std::stoi(children[1]->GP()->value()); } catch (const std::exception &) { ThrowParsingException(concat("buildCS: invalid CS axis count: ", children[1]->GP()->value())); } } else { const char *csTypeCStr = ""; const auto &parentNodeName = parentNode->GP()->value(); if (ci_equal(parentNodeName, WKTConstants::GEOCCS)) { csTypeCStr = "Cartesian"; isGeocentric = true; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { ThrowParsingExceptionMissingUNIT(); } return CartesianCS::createGeocentric(unit); } } else if (ci_equal(parentNodeName, WKTConstants::GEOGCS)) { csTypeCStr = "Ellipsoidal"; if (axisCount == 0) { // Missing axis with GEOGCS ? Presumably Long/Lat order // implied auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::ANGULAR); if (unit == UnitOfMeasure::NONE) { ThrowParsingExceptionMissingUNIT(); } // WKT1 --> long/lat return EllipsoidalCS::createLongitudeLatitude(unit); } } else if (ci_equal(parentNodeName, WKTConstants::BASEGEODCRS) || ci_equal(parentNodeName, WKTConstants::BASEGEOGCRS)) { csTypeCStr = "Ellipsoidal"; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::ANGULAR); if (unit == UnitOfMeasure::NONE) { unit = defaultAngularUnit; } // WKT2 --> presumably lat/long return EllipsoidalCS::createLatitudeLongitude(unit); } } else if (ci_equal(parentNodeName, WKTConstants::PROJCS) || ci_equal(parentNodeName, WKTConstants::BASEPROJCRS) || ci_equal(parentNodeName, WKTConstants::BASEENGCRS)) { csTypeCStr = "Cartesian"; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { if (ci_equal(parentNodeName, WKTConstants::PROJCS)) { ThrowParsingExceptionMissingUNIT(); } else { unit = UnitOfMeasure::METRE; } } return CartesianCS::createEastingNorthing(unit); } } else if (ci_equal(parentNodeName, WKTConstants::VERT_CS) || ci_equal(parentNodeName, WKTConstants::BASEVERTCRS)) { csTypeCStr = "vertical"; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { if (ci_equal(parentNodeName, WKTConstants::VERT_CS)) { ThrowParsingExceptionMissingUNIT(); } else { unit = UnitOfMeasure::METRE; } } return VerticalCS::createGravityRelatedHeight(unit); } } else if (ci_equal(parentNodeName, WKTConstants::LOCAL_CS)) { if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { unit = UnitOfMeasure::METRE; } return CartesianCS::createEastingNorthing(unit); } else if (axisCount == 1) { csTypeCStr = "vertical"; } else if (axisCount == 2) { csTypeCStr = "Cartesian"; } else { throw ParsingException( "buildCS: unexpected AXIS count for LOCAL_CS"); } } else if (ci_equal(parentNodeName, WKTConstants::BASEPARAMCRS)) { csTypeCStr = "parametric"; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::LINEAR); if (unit == UnitOfMeasure::NONE) { unit = UnitOfMeasure("unknown", 1, UnitOfMeasure::Type::PARAMETRIC); } return ParametricCS::create( emptyPropertyMap, CoordinateSystemAxis::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unknown parametric"), std::string(), AxisDirection::UNSPECIFIED, unit)); } } else if (ci_equal(parentNodeName, WKTConstants::BASETIMECRS)) { csTypeCStr = "temporal"; if (axisCount == 0) { auto unit = buildUnitInSubNode(parentNode, UnitOfMeasure::Type::TIME); if (unit == UnitOfMeasure::NONE) { unit = UnitOfMeasure("unknown", 1, UnitOfMeasure::Type::TIME); } return DateTimeTemporalCS::create( emptyPropertyMap, CoordinateSystemAxis::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unknown temporal"), std::string(), AxisDirection::FUTURE, unit)); } } else { // Shouldn't happen normally throw ParsingException( concat("buildCS: unexpected parent node: ", parentNodeName)); } csType = csTypeCStr; } if (axisCount != 1 && axisCount != 2 && axisCount != 3) { throw buildParsingExceptionInvalidAxisCount(csType); } if (numberOfAxis != axisCount) { throw ParsingException("buildCS: declared number of axis by CS node " "and number of AXIS are inconsistent"); } const auto unitType = ci_equal(csType, "ellipsoidal") ? UnitOfMeasure::Type::ANGULAR : ci_equal(csType, "ordinal") ? UnitOfMeasure::Type::NONE : ci_equal(csType, "parametric") ? UnitOfMeasure::Type::PARAMETRIC : ci_equal(csType, "Cartesian") || ci_equal(csType, "vertical") ? UnitOfMeasure::Type::LINEAR : (ci_equal(csType, "temporal") || ci_equal(csType, "TemporalDateTime") || ci_equal(csType, "TemporalCount") || ci_equal(csType, "TemporalMeasure")) ? UnitOfMeasure::Type::TIME : UnitOfMeasure::Type::UNKNOWN; UnitOfMeasure unit = buildUnitInSubNode(parentNode, unitType); std::vector axisList; for (int i = 0; i < axisCount; i++) { axisList.emplace_back( buildAxis(parentNode->GP()->lookForChild(WKTConstants::AXIS, i), unit, unitType, isGeocentric, i + 1)); }; const PropertyMap &csMap = emptyPropertyMap; if (ci_equal(csType, "ellipsoidal")) { if (axisCount == 2) { return EllipsoidalCS::create(csMap, axisList[0], axisList[1]); } else if (axisCount == 3) { return EllipsoidalCS::create(csMap, axisList[0], axisList[1], axisList[2]); } } else if (ci_equal(csType, "Cartesian")) { if (axisCount == 2) { return CartesianCS::create(csMap, axisList[0], axisList[1]); } else if (axisCount == 3) { return CartesianCS::create(csMap, axisList[0], axisList[1], axisList[2]); } } else if (ci_equal(csType, "vertical")) { if (axisCount == 1) { return VerticalCS::create(csMap, axisList[0]); } } else if (ci_equal(csType, "spherical")) { if (axisCount == 3) { return SphericalCS::create(csMap, axisList[0], axisList[1], axisList[2]); } } else if (ci_equal(csType, "ordinal")) { // WKT2-2019 return OrdinalCS::create(csMap, axisList); } else if (ci_equal(csType, "parametric")) { if (axisCount == 1) { return ParametricCS::create(csMap, axisList[0]); } } else if (ci_equal(csType, "temporal")) { // WKT2-2015 if (axisCount == 1) { return DateTimeTemporalCS::create( csMap, axisList[0]); // FIXME: there are 3 possible subtypes of // TemporalCS } } else if (ci_equal(csType, "TemporalDateTime")) { // WKT2-2019 if (axisCount == 1) { return DateTimeTemporalCS::create(csMap, axisList[0]); } } else if (ci_equal(csType, "TemporalCount")) { // WKT2-2019 if (axisCount == 1) { return TemporalCountCS::create(csMap, axisList[0]); } } else if (ci_equal(csType, "TemporalMeasure")) { // WKT2-2019 if (axisCount == 1) { return TemporalMeasureCS::create(csMap, axisList[0]); } } else { throw ParsingException(concat("unhandled CS type: ", csType)); } throw buildParsingExceptionInvalidAxisCount(csType); } // --------------------------------------------------------------------------- void WKTParser::Private::addExtensionProj4ToProp(const WKTNode::Private *nodeP, PropertyMap &props) { auto &extensionNode = nodeP->lookForChild(WKTConstants::EXTENSION); const auto &extensionChildren = extensionNode->GP()->children(); if (extensionChildren.size() == 2) { if (ci_equal(stripQuotes(extensionChildren[0]), "PROJ4")) { props.set("EXTENSION_PROJ4", stripQuotes(extensionChildren[1])); } } } // --------------------------------------------------------------------------- GeodeticCRSNNPtr WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &datumNode = nodeP->lookForChild( WKTConstants::DATUM, WKTConstants::GEODETICDATUM, WKTConstants::TRF); auto &ensembleNode = nodeP->lookForChild(WKTConstants::ENSEMBLE); if (isNull(datumNode) && isNull(ensembleNode)) { throw ParsingException("Missing DATUM or ENSEMBLE node"); } auto &dynamicNode = nodeP->lookForChild(WKTConstants::DYNAMIC); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); const auto &nodeName = nodeP->value(); if (isNull(csNode) && !ci_equal(nodeName, WKTConstants::GEOGCS) && !ci_equal(nodeName, WKTConstants::GEOCCS) && !ci_equal(nodeName, WKTConstants::BASEGEODCRS) && !ci_equal(nodeName, WKTConstants::BASEGEOGCRS)) { ThrowMissing(WKTConstants::CS_); } auto &primeMeridianNode = nodeP->lookForChild(WKTConstants::PRIMEM, WKTConstants::PRIMEMERIDIAN); if (isNull(primeMeridianNode)) { // PRIMEM is required in WKT1 if (ci_equal(nodeName, WKTConstants::GEOGCS) || ci_equal(nodeName, WKTConstants::GEOCCS)) { emitRecoverableWarning(nodeName + " should have a PRIMEM node"); } } auto angularUnit = buildUnitInSubNode(node, ci_equal(nodeName, WKTConstants::GEOGCS) ? UnitOfMeasure::Type::ANGULAR : UnitOfMeasure::Type::UNKNOWN); if (angularUnit.type() != UnitOfMeasure::Type::ANGULAR) { angularUnit = UnitOfMeasure::NONE; } auto primeMeridian = !isNull(primeMeridianNode) ? buildPrimeMeridian(primeMeridianNode, angularUnit) : PrimeMeridian::GREENWICH; if (angularUnit == UnitOfMeasure::NONE) { angularUnit = primeMeridian->longitude().unit(); } auto props = buildProperties(node); addExtensionProj4ToProp(nodeP, props); // No explicit AXIS node ? (WKT1) if (isNull(nodeP->lookForChild(WKTConstants::AXIS))) { props.set("IMPLICIT_CS", true); } auto datum = !isNull(datumNode) ? buildGeodeticReferenceFrame(datumNode, primeMeridian, dynamicNode) .as_nullable() : nullptr; auto datumEnsemble = !isNull(ensembleNode) ? buildDatumEnsemble(ensembleNode, primeMeridian, true) .as_nullable() : nullptr; auto cs = buildCS(csNode, node, angularUnit); auto ellipsoidalCS = nn_dynamic_pointer_cast(cs); if (ellipsoidalCS) { if (ci_equal(nodeName, WKTConstants::GEOCCS)) { throw ParsingException("ellipsoidal CS not expected in GEOCCS"); } try { auto crs = GeographicCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(ellipsoidalCS)); // In case of missing CS node, or to check it, query the coordinate // system from the DB if possible (typically for the baseCRS of a // ProjectedCRS) if (!crs->identifiers().empty() && dbContext_) { GeographicCRSPtr dbCRS; try { const auto &id = crs->identifiers()[0]; auto authFactory = AuthorityFactory::create( NN_NO_CHECK(dbContext_), *id->codeSpace()); dbCRS = authFactory->createGeographicCRS(id->code()) .as_nullable(); } catch (const util::Exception &) { } if (dbCRS && (!isNull(csNode) || node->countChildrenOfName(WKTConstants::AXIS) != 0) && !ellipsoidalCS->_isEquivalentTo( dbCRS->coordinateSystem().get(), util::IComparable::Criterion::EQUIVALENT)) { emitRecoverableWarning( "Coordinate system of GeographicCRS in the WKT " "definition is different from the one of the " "authority. Unsetting the identifier to avoid " "confusion"); props.unset(Identifier::CODESPACE_KEY); props.unset(Identifier::AUTHORITY_KEY); props.unset(IdentifiedObject::IDENTIFIERS_KEY); crs = GeographicCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(ellipsoidalCS)); } else if (dbCRS) { crs = GeographicCRS::create(props, datum, datumEnsemble, dbCRS->coordinateSystem()); } } return crs; } catch (const util::Exception &e) { throw ParsingException(std::string("buildGeodeticCRS: ") + e.what()); } } else if (ci_equal(nodeName, WKTConstants::GEOGCRS) || ci_equal(nodeName, WKTConstants::GEOGRAPHICCRS) || ci_equal(nodeName, WKTConstants::BASEGEOGCRS)) { // This is a WKT2-2019 GeographicCRS. An ellipsoidal CS is expected throw ParsingException(concat("ellipsoidal CS expected, but found ", cs->getWKT2Type(true))); } auto cartesianCS = nn_dynamic_pointer_cast(cs); if (cartesianCS) { if (cartesianCS->axisList().size() != 3) { throw ParsingException( "Cartesian CS for a GeodeticCRS should have 3 axis"); } try { return GeodeticCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(cartesianCS)); } catch (const util::Exception &e) { throw ParsingException(std::string("buildGeodeticCRS: ") + e.what()); } } auto sphericalCS = nn_dynamic_pointer_cast(cs); if (sphericalCS) { try { return GeodeticCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(sphericalCS)); } catch (const util::Exception &e) { throw ParsingException(std::string("buildGeodeticCRS: ") + e.what()); } } throw ParsingException( concat("unhandled CS type: ", cs->getWKT2Type(true))); } // --------------------------------------------------------------------------- CRSNNPtr WKTParser::Private::buildDerivedGeodeticCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseGeodCRSNode = nodeP->lookForChild(WKTConstants::BASEGEODCRS, WKTConstants::BASEGEOGCRS); // given the constraints enforced on calling code path assert(!isNull(baseGeodCRSNode)); auto baseGeodCRS = buildGeodeticCRS(baseGeodCRSNode); auto &derivingConversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(derivingConversionNode)) { ThrowMissing(WKTConstants::DERIVINGCONVERSION); } auto derivingConversion = buildConversion( derivingConversionNode, UnitOfMeasure::NONE, UnitOfMeasure::NONE); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); if (isNull(csNode)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); auto ellipsoidalCS = nn_dynamic_pointer_cast(cs); if (ellipsoidalCS) { return DerivedGeographicCRS::create(buildProperties(node), baseGeodCRS, derivingConversion, NN_NO_CHECK(ellipsoidalCS)); } else if (ci_equal(nodeP->value(), WKTConstants::GEOGCRS)) { // This is a WKT2-2019 GeographicCRS. An ellipsoidal CS is expected throw ParsingException(concat("ellipsoidal CS expected, but found ", cs->getWKT2Type(true))); } auto cartesianCS = nn_dynamic_pointer_cast(cs); if (cartesianCS) { if (cartesianCS->axisList().size() != 3) { throw ParsingException( "Cartesian CS for a GeodeticCRS should have 3 axis"); } return DerivedGeodeticCRS::create(buildProperties(node), baseGeodCRS, derivingConversion, NN_NO_CHECK(cartesianCS)); } auto sphericalCS = nn_dynamic_pointer_cast(cs); if (sphericalCS) { return DerivedGeodeticCRS::create(buildProperties(node), baseGeodCRS, derivingConversion, NN_NO_CHECK(sphericalCS)); } throw ParsingException( concat("unhandled CS type: ", cs->getWKT2Type(true))); } // --------------------------------------------------------------------------- UnitOfMeasure WKTParser::Private::guessUnitForParameter( const std::string ¶mName, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { UnitOfMeasure unit; // scale must be first because of 'Scale factor on pseudo standard parallel' if (ci_find(paramName, "scale") != std::string::npos) { unit = UnitOfMeasure::SCALE_UNITY; } else if (ci_find(paramName, "latitude") != std::string::npos || ci_find(paramName, "longitude") != std::string::npos || ci_find(paramName, "meridian") != std::string::npos || ci_find(paramName, "parallel") != std::string::npos || ci_find(paramName, "azimuth") != std::string::npos || ci_find(paramName, "angle") != std::string::npos || ci_find(paramName, "heading") != std::string::npos) { unit = defaultAngularUnit; } else if (ci_find(paramName, "easting") != std::string::npos || ci_find(paramName, "northing") != std::string::npos || ci_find(paramName, "height") != std::string::npos) { unit = defaultLinearUnit; } return unit; } // --------------------------------------------------------------------------- void WKTParser::Private::consumeParameters( const WKTNodeNNPtr &node, bool isAbridged, std::vector ¶meters, std::vector &values, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { for (const auto &childNode : node->GP()->children()) { const auto &childNodeChildren = childNode->GP()->children(); if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { if (childNodeChildren.size() < 2) { ThrowNotEnoughChildren(childNode->GP()->value()); } parameters.push_back( OperationParameter::create(buildProperties(childNode))); const auto ¶mValue = childNodeChildren[1]->GP()->value(); if (!paramValue.empty() && paramValue[0] == '"') { values.push_back( ParameterValue::create(stripQuotes(childNodeChildren[1]))); } else { try { double val = asDouble(childNodeChildren[1]); auto unit = buildUnitInSubNode(childNode); if (unit == UnitOfMeasure::NONE) { const auto ¶mName = childNodeChildren[0]->GP()->value(); unit = guessUnitForParameter( paramName, defaultLinearUnit, defaultAngularUnit); } if (isAbridged) { const auto ¶mName = parameters.back()->nameStr(); int paramEPSGCode = 0; const auto ¶mIds = parameters.back()->identifiers(); if (paramIds.size() == 1 && ci_equal(*(paramIds[0]->codeSpace()), Identifier::EPSG)) { paramEPSGCode = ::atoi(paramIds[0]->code().c_str()); } const common::UnitOfMeasure *pUnit = nullptr; if (OperationParameterValue::convertFromAbridged( paramName, val, pUnit, paramEPSGCode)) { unit = *pUnit; parameters.back() = OperationParameter::create( buildProperties(childNode) .set(Identifier::CODESPACE_KEY, Identifier::EPSG) .set(Identifier::CODE_KEY, paramEPSGCode)); } } values.push_back( ParameterValue::create(Measure(val, unit))); } catch (const std::exception &) { throw ParsingException(concat( "unhandled parameter value type : ", paramValue)); } } } else if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETERFILE)) { if (childNodeChildren.size() < 2) { ThrowNotEnoughChildren(childNode->GP()->value()); } parameters.push_back( OperationParameter::create(buildProperties(childNode))); values.push_back(ParameterValue::createFilename( stripQuotes(childNodeChildren[1]))); } } } // --------------------------------------------------------------------------- ConversionNNPtr WKTParser::Private::buildConversion(const WKTNodeNNPtr &node, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { auto &methodNode = node->GP()->lookForChild(WKTConstants::METHOD, WKTConstants::PROJECTION); if (isNull(methodNode)) { ThrowMissing(WKTConstants::METHOD); } if (methodNode->GP()->childrenSize() == 0) { ThrowNotEnoughChildren(WKTConstants::METHOD); } std::vector parameters; std::vector values; consumeParameters(node, false, parameters, values, defaultLinearUnit, defaultAngularUnit); auto &convProps = buildProperties(node); auto &methodProps = buildProperties(methodNode); std::string convName; std::string methodName; if (convProps.getStringValue(IdentifiedObject::NAME_KEY, convName) && methodProps.getStringValue(IdentifiedObject::NAME_KEY, methodName) && starts_with(convName, "Inverse of ") && starts_with(methodName, "Inverse of ")) { auto &invConvProps = buildProperties(node, true); auto &invMethodProps = buildProperties(methodNode, true); return NN_NO_CHECK(util::nn_dynamic_pointer_cast( Conversion::create(invConvProps, invMethodProps, parameters, values) ->inverse())); } return Conversion::create(convProps, methodProps, parameters, values); } // --------------------------------------------------------------------------- TransformationNNPtr WKTParser::Private::buildCoordinateOperation(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &methodNode = nodeP->lookForChild(WKTConstants::METHOD); if (isNull(methodNode)) { ThrowMissing(WKTConstants::METHOD); } if (methodNode->GP()->childrenSize() == 0) { ThrowNotEnoughChildren(WKTConstants::METHOD); } auto &sourceCRSNode = nodeP->lookForChild(WKTConstants::SOURCECRS); if (/*isNull(sourceCRSNode) ||*/ sourceCRSNode->GP()->childrenSize() != 1) { ThrowMissing(WKTConstants::SOURCECRS); } auto sourceCRS = buildCRS(sourceCRSNode->GP()->children()[0]); if (!sourceCRS) { throw ParsingException("Invalid content in SOURCECRS node"); } auto &targetCRSNode = nodeP->lookForChild(WKTConstants::TARGETCRS); if (/*isNull(targetCRSNode) ||*/ targetCRSNode->GP()->childrenSize() != 1) { ThrowMissing(WKTConstants::TARGETCRS); } auto targetCRS = buildCRS(targetCRSNode->GP()->children()[0]); if (!targetCRS) { throw ParsingException("Invalid content in TARGETCRS node"); } auto &interpolationCRSNode = nodeP->lookForChild(WKTConstants::INTERPOLATIONCRS); CRSPtr interpolationCRS; if (/*!isNull(interpolationCRSNode) && */ interpolationCRSNode->GP() ->childrenSize() == 1) { interpolationCRS = buildCRS(interpolationCRSNode->GP()->children()[0]); } std::vector parameters; std::vector values; auto defaultLinearUnit = UnitOfMeasure::NONE; auto defaultAngularUnit = UnitOfMeasure::NONE; consumeParameters(node, false, parameters, values, defaultLinearUnit, defaultAngularUnit); std::vector accuracies; auto &accuracyNode = nodeP->lookForChild(WKTConstants::OPERATIONACCURACY); if (/*!isNull(accuracyNode) && */ accuracyNode->GP()->childrenSize() == 1) { accuracies.push_back(PositionalAccuracy::create( stripQuotes(accuracyNode->GP()->children()[0]))); } return Transformation::create(buildProperties(node), NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), interpolationCRS, buildProperties(methodNode), parameters, values, accuracies); } // --------------------------------------------------------------------------- ConcatenatedOperationNNPtr WKTParser::Private::buildConcatenatedOperation(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &sourceCRSNode = nodeP->lookForChild(WKTConstants::SOURCECRS); if (/*isNull(sourceCRSNode) ||*/ sourceCRSNode->GP()->childrenSize() != 1) { ThrowMissing(WKTConstants::SOURCECRS); } auto sourceCRS = buildCRS(sourceCRSNode->GP()->children()[0]); if (!sourceCRS) { throw ParsingException("Invalid content in SOURCECRS node"); } auto &targetCRSNode = nodeP->lookForChild(WKTConstants::TARGETCRS); if (/*isNull(targetCRSNode) ||*/ targetCRSNode->GP()->childrenSize() != 1) { ThrowMissing(WKTConstants::TARGETCRS); } auto targetCRS = buildCRS(targetCRSNode->GP()->children()[0]); if (!targetCRS) { throw ParsingException("Invalid content in TARGETCRS node"); } std::vector operations; for (const auto &childNode : nodeP->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::STEP)) { if (childNode->GP()->childrenSize() != 1) { throw ParsingException("Invalid content in STEP node"); } auto op = nn_dynamic_pointer_cast( build(childNode->GP()->children()[0])); if (!op) { throw ParsingException("Invalid content in STEP node"); } operations.emplace_back(NN_NO_CHECK(op)); } } ConcatenatedOperation::fixStepsDirection( NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), operations); try { return ConcatenatedOperation::create( buildProperties(node), operations, std::vector()); } catch (const InvalidOperation &e) { throw ParsingException( std::string("Cannot build concatenated operation: ") + e.what()); } } // --------------------------------------------------------------------------- bool WKTParser::Private::hasWebMercPROJ4String( const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode) { if (projectionNode->GP()->childrenSize() == 0) { ThrowNotEnoughChildren(WKTConstants::PROJECTION); } const std::string wkt1ProjectionName = stripQuotes(projectionNode->GP()->children()[0]); auto &extensionNode = projCRSNode->lookForChild(WKTConstants::EXTENSION); if (metadata::Identifier::isEquivalentName(wkt1ProjectionName.c_str(), "Mercator_1SP") && projCRSNode->countChildrenOfName("center_latitude") == 0) { // Hack to detect the hacky way of encodign webmerc in GDAL WKT1 // with a EXTENSION["PROJ4", "+proj=merc +a=6378137 +b=6378137 // +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m // +nadgrids=@null +wktext +no_defs"] node if (extensionNode && extensionNode->GP()->childrenSize() == 2 && ci_equal(stripQuotes(extensionNode->GP()->children()[0]), "PROJ4")) { std::string projString = stripQuotes(extensionNode->GP()->children()[1]); if (projString.find("+proj=merc") != std::string::npos && projString.find("+a=6378137") != std::string::npos && projString.find("+b=6378137") != std::string::npos && projString.find("+lon_0=0") != std::string::npos && projString.find("+x_0=0") != std::string::npos && projString.find("+y_0=0") != std::string::npos && projString.find("+nadgrids=@null") != std::string::npos && (projString.find("+lat_ts=") == std::string::npos || projString.find("+lat_ts=0") != std::string::npos) && (projString.find("+k=") == std::string::npos || projString.find("+k=1") != std::string::npos) && (projString.find("+units=") == std::string::npos || projString.find("+units=m") != std::string::npos)) { return true; } } } return false; } // --------------------------------------------------------------------------- ConversionNNPtr WKTParser::Private::buildProjectionFromESRI( const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { const std::string esriProjectionName = stripQuotes(projectionNode->GP()->children()[0]); // Lambert_Conformal_Conic or Krovak may map to different WKT2 methods // depending // on the parameters / their values const auto esriMappings = getMappingsFromESRI(esriProjectionName); if (esriMappings.empty()) { return buildProjectionStandard(projCRSNode, projectionNode, defaultLinearUnit, defaultAngularUnit); } struct ci_less_struct { bool operator()(const std::string &lhs, const std::string &rhs) const noexcept { return ci_less(lhs, rhs); } }; // Build a map of present parameters std::map mapParamNameToValue; for (const auto &childNode : projCRSNode->GP()->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { const auto &childNodeChildren = childNode->GP()->children(); if (childNodeChildren.size() < 2) { ThrowNotEnoughChildren(WKTConstants::PARAMETER); } const std::string parameterName(stripQuotes(childNodeChildren[0])); const auto ¶mValue = childNodeChildren[1]->GP()->value(); mapParamNameToValue[parameterName] = paramValue; } } // Compare parameters present with the ones expected in the mapping const ESRIMethodMapping *esriMapping = esriMappings[0]; int bestMatchCount = -1; for (const auto &mapping : esriMappings) { int matchCount = 0; for (const auto *param = mapping->params; param->esri_name; ++param) { auto iter = mapParamNameToValue.find(param->esri_name); if (iter != mapParamNameToValue.end()) { if (param->wkt2_name == nullptr) { try { if (param->fixed_value == io::asDouble(iter->second)) { matchCount++; } } catch (const std::exception &) { } } else { matchCount++; } } } if (matchCount > bestMatchCount) { esriMapping = mapping; bestMatchCount = matchCount; } } std::map mapWKT2NameToESRIName; for (const auto *param = esriMapping->params; param->esri_name; ++param) { if (param->wkt2_name) { mapWKT2NameToESRIName[param->wkt2_name] = param->esri_name; } } const char *projectionMethodWkt2Name = esriMapping->wkt2_name; if (ci_equal(esriProjectionName, "Krovak")) { const std::string projCRSName = stripQuotes(projCRSNode->GP()->children()[0]); if (projCRSName.find("_East_North") != std::string::npos) { projectionMethodWkt2Name = EPSG_NAME_METHOD_KROVAK_NORTH_ORIENTED; } } const auto *wkt2_mapping = getMapping(projectionMethodWkt2Name); if (ci_equal(esriProjectionName, "Stereographic")) { try { if (std::fabs(io::asDouble( mapParamNameToValue["Latitude_Of_Origin"])) == 90.0) { wkt2_mapping = getMapping(EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A); } } catch (const std::exception &) { } } assert(wkt2_mapping); PropertyMap propertiesMethod; propertiesMethod.set(IdentifiedObject::NAME_KEY, wkt2_mapping->wkt2_name); if (wkt2_mapping->epsg_code != 0) { propertiesMethod.set(Identifier::CODE_KEY, wkt2_mapping->epsg_code); propertiesMethod.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } std::vector parameters; std::vector values; if (wkt2_mapping->epsg_code == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL && ci_equal(esriProjectionName, "Plate_Carree")) { // Add a fixed Latitude of 1st parallel = 0 so as to have all // parameters expected by Equidistant Cylindrical. mapWKT2NameToESRIName[EPSG_NAME_PARAMETER_LATITUDE_1ST_STD_PARALLEL] = "Standard_Parallel_1"; mapParamNameToValue["Standard_Parallel_1"] = "0"; } else if ((wkt2_mapping->epsg_code == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A || wkt2_mapping->epsg_code == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B) && !ci_equal(esriProjectionName, "Rectified_Skew_Orthomorphic_Natural_Origin") && !ci_equal(esriProjectionName, "Rectified_Skew_Orthomorphic_Center")) { // ESRI WKT lacks the angle to skew grid // Take it from the azimuth value mapWKT2NameToESRIName [EPSG_NAME_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID] = "Azimuth"; } for (int i = 0; wkt2_mapping->params[i] != nullptr; i++) { const auto *paramMapping = wkt2_mapping->params[i]; auto iter = mapWKT2NameToESRIName.find(paramMapping->wkt2_name); if (iter == mapWKT2NameToESRIName.end()) { continue; } const auto &esriParamName = iter->second; auto iter2 = mapParamNameToValue.find(esriParamName); auto mapParamNameToValueEnd = mapParamNameToValue.end(); if (iter2 == mapParamNameToValueEnd) { // In case we don't find a direct match, try the aliases for (iter2 = mapParamNameToValue.begin(); iter2 != mapParamNameToValueEnd; ++iter2) { if (areEquivalentParameters(iter2->first, esriParamName)) { break; } } if (iter2 == mapParamNameToValueEnd) { continue; } } PropertyMap propertiesParameter; propertiesParameter.set(IdentifiedObject::NAME_KEY, paramMapping->wkt2_name); if (paramMapping->epsg_code != 0) { propertiesParameter.set(Identifier::CODE_KEY, paramMapping->epsg_code); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } parameters.push_back(OperationParameter::create(propertiesParameter)); try { double val = io::asDouble(iter2->second); auto unit = guessUnitForParameter( paramMapping->wkt2_name, defaultLinearUnit, defaultAngularUnit); values.push_back(ParameterValue::create(Measure(val, unit))); } catch (const std::exception &) { throw ParsingException( concat("unhandled parameter value type : ", iter2->second)); } } return Conversion::create( PropertyMap().set(IdentifiedObject::NAME_KEY, esriProjectionName == "Gauss_Kruger" ? "unnnamed (Gauss Kruger)" : "unnamed"), propertiesMethod, parameters, values) ->identify(); } // --------------------------------------------------------------------------- ConversionNNPtr WKTParser::Private::buildProjection(const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { if (projectionNode->GP()->childrenSize() == 0) { ThrowNotEnoughChildren(WKTConstants::PROJECTION); } if (esriStyle_) { return buildProjectionFromESRI(projCRSNode, projectionNode, defaultLinearUnit, defaultAngularUnit); } return buildProjectionStandard(projCRSNode, projectionNode, defaultLinearUnit, defaultAngularUnit); } // --------------------------------------------------------------------------- std::string WKTParser::Private::projectionGetParameter(const WKTNodeNNPtr &projCRSNode, const char *paramName) { for (const auto &childNode : projCRSNode->GP()->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { const auto &childNodeChildren = childNode->GP()->children(); if (childNodeChildren.size() == 2 && metadata::Identifier::isEquivalentName( stripQuotes(childNodeChildren[0]).c_str(), paramName)) { return childNodeChildren[1]->GP()->value(); } } } return std::string(); } // --------------------------------------------------------------------------- ConversionNNPtr WKTParser::Private::buildProjectionStandard( const WKTNodeNNPtr &projCRSNode, const WKTNodeNNPtr &projectionNode, const UnitOfMeasure &defaultLinearUnit, const UnitOfMeasure &defaultAngularUnit) { std::string wkt1ProjectionName = stripQuotes(projectionNode->GP()->children()[0]); std::vector parameters; std::vector values; bool tryToIdentifyWKT1Method = true; auto &extensionNode = projCRSNode->lookForChild(WKTConstants::EXTENSION); const auto &extensionChildren = extensionNode->GP()->children(); bool gdal_3026_hack = false; if (metadata::Identifier::isEquivalentName(wkt1ProjectionName.c_str(), "Mercator_1SP") && projectionGetParameter(projCRSNode, "center_latitude").empty()) { // Hack for https://trac.osgeo.org/gdal/ticket/3026 std::string lat0( projectionGetParameter(projCRSNode, "latitude_of_origin")); if (!lat0.empty() && lat0 != "0" && lat0 != "0.0") { wkt1ProjectionName = "Mercator_2SP"; gdal_3026_hack = true; } else { // The latitude of origin, which should always be zero, is // missing // in GDAL WKT1, but provisionned in the EPSG Mercator_1SP // definition, // so add it manually. PropertyMap propertiesParameter; propertiesParameter.set(IdentifiedObject::NAME_KEY, "Latitude of natural origin"); propertiesParameter.set(Identifier::CODE_KEY, 8801); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); parameters.push_back( OperationParameter::create(propertiesParameter)); values.push_back( ParameterValue::create(Measure(0, UnitOfMeasure::DEGREE))); } } else if (metadata::Identifier::isEquivalentName( wkt1ProjectionName.c_str(), "Polar_Stereographic")) { std::map mapParameters; for (const auto &childNode : projCRSNode->GP()->children()) { const auto &childNodeChildren = childNode->GP()->children(); if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER) && childNodeChildren.size() == 2) { const std::string wkt1ParameterName( stripQuotes(childNodeChildren[0])); try { double val = asDouble(childNodeChildren[1]); auto unit = guessUnitForParameter(wkt1ParameterName, defaultLinearUnit, defaultAngularUnit); mapParameters.insert(std::pair( tolower(wkt1ParameterName), Measure(val, unit))); } catch (const std::exception &) { } } } Measure latitudeOfOrigin = mapParameters["latitude_of_origin"]; Measure centralMeridian = mapParameters["central_meridian"]; Measure scaleFactorFromMap = mapParameters["scale_factor"]; Measure scaleFactor((scaleFactorFromMap.unit() == UnitOfMeasure::NONE) ? Measure(1.0, UnitOfMeasure::SCALE_UNITY) : scaleFactorFromMap); Measure falseEasting = mapParameters["false_easting"]; Measure falseNorthing = mapParameters["false_northing"]; if (latitudeOfOrigin.unit() != UnitOfMeasure::NONE && scaleFactor.getSIValue() == 1.0) { return Conversion::createPolarStereographicVariantB( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(latitudeOfOrigin.value(), latitudeOfOrigin.unit()), Angle(centralMeridian.value(), centralMeridian.unit()), Length(falseEasting.value(), falseEasting.unit()), Length(falseNorthing.value(), falseNorthing.unit())); } if (latitudeOfOrigin.unit() != UnitOfMeasure::NONE && std::fabs(std::fabs(latitudeOfOrigin.convertToUnit( UnitOfMeasure::DEGREE)) - 90.0) < 1e-10) { return Conversion::createPolarStereographicVariantA( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(latitudeOfOrigin.value(), latitudeOfOrigin.unit()), Angle(centralMeridian.value(), centralMeridian.unit()), Scale(scaleFactor.value(), scaleFactor.unit()), Length(falseEasting.value(), falseEasting.unit()), Length(falseNorthing.value(), falseNorthing.unit())); } tryToIdentifyWKT1Method = false; // Import GDAL PROJ4 extension nodes } else if (extensionChildren.size() == 2 && ci_equal(stripQuotes(extensionChildren[0]), "PROJ4")) { std::string projString = stripQuotes(extensionChildren[1]); if (starts_with(projString, "+proj=")) { if (projString.find(" +type=crs") == std::string::npos) { projString += " +type=crs"; } try { auto projObj = PROJStringParser().createFromPROJString(projString); auto projObjCrs = nn_dynamic_pointer_cast(projObj); if (projObjCrs) { return projObjCrs->derivingConversion(); } } catch (const io::ParsingException &) { } } } std::string projectionName(wkt1ProjectionName); const MethodMapping *mapping = tryToIdentifyWKT1Method ? getMappingFromWKT1(projectionName) : nullptr; // For Krovak, we need to look at axis to decide between the Krovak and // Krovak East-North Oriented methods if (ci_equal(projectionName, "Krovak") && projCRSNode->countChildrenOfName(WKTConstants::AXIS) == 2 && &buildAxis( projCRSNode->GP()->lookForChild(WKTConstants::AXIS, 0), defaultLinearUnit, UnitOfMeasure::Type::LINEAR, false, 1)->direction() == &AxisDirection::SOUTH && &buildAxis( projCRSNode->GP()->lookForChild(WKTConstants::AXIS, 1), defaultLinearUnit, UnitOfMeasure::Type::LINEAR, false, 2)->direction() == &AxisDirection::WEST) { mapping = getMapping(EPSG_CODE_METHOD_KROVAK); } PropertyMap propertiesMethod; if (mapping) { projectionName = mapping->wkt2_name; if (mapping->epsg_code != 0) { propertiesMethod.set(Identifier::CODE_KEY, mapping->epsg_code); propertiesMethod.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } } propertiesMethod.set(IdentifiedObject::NAME_KEY, projectionName); std::vector foundParameters; if (mapping) { size_t countParams = 0; while (mapping->params[countParams] != nullptr) { ++countParams; } foundParameters.resize(countParams); } for (const auto &childNode : projCRSNode->GP()->children()) { if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) { const auto &childNodeChildren = childNode->GP()->children(); if (childNodeChildren.size() < 2) { ThrowNotEnoughChildren(WKTConstants::PARAMETER); } const auto ¶mValue = childNodeChildren[1]->GP()->value(); PropertyMap propertiesParameter; const std::string wkt1ParameterName( stripQuotes(childNodeChildren[0])); std::string parameterName(wkt1ParameterName); if (gdal_3026_hack) { if (ci_equal(parameterName, "latitude_of_origin")) { parameterName = "standard_parallel_1"; } else if (ci_equal(parameterName, "scale_factor") && paramValue == "1") { continue; } } auto *paramMapping = mapping ? getMappingFromWKT1(mapping, parameterName) : nullptr; if (mapping && mapping->epsg_code == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && ci_equal(parameterName, "latitude_of_origin")) { for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { if (mapping->params[idx]->epsg_code == EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) { foundParameters[idx] = true; break; } } parameterName = EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN; propertiesParameter.set( Identifier::CODE_KEY, EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } else if (paramMapping) { for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { if (mapping->params[idx] == paramMapping) { foundParameters[idx] = true; break; } } parameterName = paramMapping->wkt2_name; if (paramMapping->epsg_code != 0) { propertiesParameter.set(Identifier::CODE_KEY, paramMapping->epsg_code); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } } propertiesParameter.set(IdentifiedObject::NAME_KEY, parameterName); parameters.push_back( OperationParameter::create(propertiesParameter)); try { double val = io::asDouble(paramValue); auto unit = guessUnitForParameter( wkt1ParameterName, defaultLinearUnit, defaultAngularUnit); values.push_back(ParameterValue::create(Measure(val, unit))); } catch (const std::exception &) { throw ParsingException( concat("unhandled parameter value type : ", paramValue)); } } } // Add back important parameters that should normally be present, but // are sometimes missing. Currently we only deal with Scale factor at // natural origin. This is to avoid a default value of 0 to slip in later. // But such WKT should be considered invalid. if (mapping) { for (size_t idx = 0; mapping->params[idx] != nullptr; ++idx) { if (!foundParameters[idx] && mapping->params[idx]->epsg_code == EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { emitRecoverableWarning( "The WKT string lacks a value " "for " EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN ". Default it to 1."); PropertyMap propertiesParameter; propertiesParameter.set( Identifier::CODE_KEY, EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); propertiesParameter.set( IdentifiedObject::NAME_KEY, EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); parameters.push_back( OperationParameter::create(propertiesParameter)); values.push_back(ParameterValue::create( Measure(1.0, UnitOfMeasure::SCALE_UNITY))); } } } return Conversion::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), propertiesMethod, parameters, values) ->identify(); } // --------------------------------------------------------------------------- static ProjectedCRSNNPtr createPseudoMercator(const PropertyMap &props) { auto conversion = Conversion::createPopularVisualisationPseudoMercator( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(0), Angle(0), Length(0), Length(0)); return ProjectedCRS::create( props, GeographicCRS::EPSG_4326, conversion, CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); } // --------------------------------------------------------------------------- ProjectedCRSNNPtr WKTParser::Private::buildProjectedCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &conversionNode = nodeP->lookForChild(WKTConstants::CONVERSION); auto &projectionNode = nodeP->lookForChild(WKTConstants::PROJECTION); if (isNull(conversionNode) && isNull(projectionNode)) { ThrowMissing(WKTConstants::CONVERSION); } auto &baseGeodCRSNode = nodeP->lookForChild(WKTConstants::BASEGEODCRS, WKTConstants::BASEGEOGCRS, WKTConstants::GEOGCS); if (isNull(baseGeodCRSNode)) { throw ParsingException( "Missing BASEGEODCRS / BASEGEOGCRS / GEOGCS node"); } auto baseGeodCRS = buildGeodeticCRS(baseGeodCRSNode); auto props = buildProperties(node); const std::string projCRSName = stripQuotes(nodeP->children()[0]); if (esriStyle_ && dbContext_) { // It is likely that the ESRI definition of EPSG:32661 (UPS North) & // EPSG:32761 (UPS South) uses the easting-northing order, instead // of the EPSG northing-easting order // so don't substitue names to avoid confusion. if (projCRSName == "UPS_North") { props.set(IdentifiedObject::NAME_KEY, "WGS 84 / UPS North (E,N)"); } else if (projCRSName == "UPS_South") { props.set(IdentifiedObject::NAME_KEY, "WGS 84 / UPS South (E,N)"); } else { std::string outTableName; std::string authNameFromAlias; std::string codeFromAlias; auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto officialName = authFactory->getOfficialNameFromAlias( projCRSName, "projected_crs", "ESRI", false, outTableName, authNameFromAlias, codeFromAlias); if (!officialName.empty()) { props.set(IdentifiedObject::NAME_KEY, officialName); } } } if (isNull(conversionNode) && hasWebMercPROJ4String(node, projectionNode)) { toWGS84Parameters_.clear(); return createPseudoMercator(props); } // WGS_84_Pseudo_Mercator: Particular case for corrupted ESRI WKT generated // by older GDAL versions // https://trac.osgeo.org/gdal/changeset/30732 // WGS_1984_Web_Mercator: deprecated ESRI:102113 if (metadata::Identifier::isEquivalentName(projCRSName.c_str(), "WGS_84_Pseudo_Mercator") || metadata::Identifier::isEquivalentName(projCRSName.c_str(), "WGS_1984_Web_Mercator")) { toWGS84Parameters_.clear(); return createPseudoMercator(props); } auto linearUnit = buildUnitInSubNode(node, UnitOfMeasure::Type::LINEAR); auto angularUnit = baseGeodCRS->coordinateSystem()->axisList()[0]->unit(); auto conversion = !isNull(conversionNode) ? buildConversion(conversionNode, linearUnit, angularUnit) : buildProjection(node, projectionNode, linearUnit, angularUnit); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); const auto &nodeValue = nodeP->value(); if (isNull(csNode) && !ci_equal(nodeValue, WKTConstants::PROJCS) && !ci_equal(nodeValue, WKTConstants::BASEPROJCRS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); auto cartesianCS = nn_dynamic_pointer_cast(cs); // No explicit AXIS node ? (WKT1) if (isNull(nodeP->lookForChild(WKTConstants::AXIS))) { props.set("IMPLICIT_CS", true); } if (isNull(csNode) && node->countChildrenOfName(WKTConstants::AXIS) == 0) { const auto methodCode = conversion->method()->getEPSGCode(); // Krovak south oriented ? if (methodCode == EPSG_CODE_METHOD_KROVAK) { cartesianCS = CartesianCS::create( PropertyMap(), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Southing), emptyString, AxisDirection::SOUTH, linearUnit), CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Westing), emptyString, AxisDirection::WEST, linearUnit)) .as_nullable(); } else if (methodCode == EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A || methodCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA) { // It is likely that the ESRI definition of EPSG:32661 (UPS North) & // EPSG:32761 (UPS South) uses the easting-northing order, instead // of the EPSG northing-easting order. // Same for WKT1_GDAL const double lat0 = conversion->parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); if (std::fabs(lat0 - 90) < 1e-10) { cartesianCS = CartesianCS::createNorthPoleEastingSouthNorthingSouth( linearUnit) .as_nullable(); } else if (std::fabs(lat0 - -90) < 1e-10) { cartesianCS = CartesianCS::createSouthPoleEastingNorthNorthingNorth( linearUnit) .as_nullable(); } } else if (methodCode == EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B) { const double lat_ts = conversion->parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_STD_PARALLEL, common::UnitOfMeasure::DEGREE); if (lat_ts > 0) { cartesianCS = CartesianCS::createNorthPoleEastingSouthNorthingSouth( linearUnit) .as_nullable(); } else if (lat_ts < 0) { cartesianCS = CartesianCS::createSouthPoleEastingNorthNorthingNorth( linearUnit) .as_nullable(); } } else if (methodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED) { cartesianCS = CartesianCS::createWestingSouthing(linearUnit).as_nullable(); } } if (!cartesianCS) { ThrowNotExpectedCSType("Cartesian"); } addExtensionProj4ToProp(nodeP, props); return ProjectedCRS::create(props, baseGeodCRS, conversion, NN_NO_CHECK(cartesianCS)); } // --------------------------------------------------------------------------- void WKTParser::Private::parseDynamic(const WKTNodeNNPtr &dynamicNode, double &frameReferenceEpoch, util::optional &modelName) { auto &frameEpochNode = dynamicNode->lookForChild(WKTConstants::FRAMEEPOCH); const auto &frameEpochChildren = frameEpochNode->GP()->children(); if (frameEpochChildren.empty()) { ThrowMissing(WKTConstants::FRAMEEPOCH); } try { frameReferenceEpoch = asDouble(frameEpochChildren[0]); } catch (const std::exception &) { throw ParsingException("Invalid FRAMEEPOCH node"); } auto &modelNode = dynamicNode->GP()->lookForChild( WKTConstants::MODEL, WKTConstants::VELOCITYGRID); const auto &modelChildren = modelNode->GP()->children(); if (modelChildren.size() == 1) { modelName = stripQuotes(modelChildren[0]); } } // --------------------------------------------------------------------------- VerticalReferenceFrameNNPtr WKTParser::Private::buildVerticalReferenceFrame( const WKTNodeNNPtr &node, const WKTNodeNNPtr &dynamicNode) { if (!isNull(dynamicNode)) { double frameReferenceEpoch = 0.0; util::optional modelName; parseDynamic(dynamicNode, frameReferenceEpoch, modelName); return DynamicVerticalReferenceFrame::create( buildProperties(node), getAnchor(node), optional(), common::Measure(frameReferenceEpoch, common::UnitOfMeasure::YEAR), modelName); } // WKT1 VERT_DATUM has a datum type after the datum name that we ignore. return VerticalReferenceFrame::create(buildProperties(node), getAnchor(node)); } // --------------------------------------------------------------------------- TemporalDatumNNPtr WKTParser::Private::buildTemporalDatum(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &calendarNode = nodeP->lookForChild(WKTConstants::CALENDAR); std::string calendar = TemporalDatum::CALENDAR_PROLEPTIC_GREGORIAN; const auto &calendarChildren = calendarNode->GP()->children(); if (calendarChildren.size() == 1) { calendar = stripQuotes(calendarChildren[0]); } auto &timeOriginNode = nodeP->lookForChild(WKTConstants::TIMEORIGIN); std::string originStr; const auto &timeOriginNodeChildren = timeOriginNode->GP()->children(); if (timeOriginNodeChildren.size() == 1) { originStr = stripQuotes(timeOriginNodeChildren[0]); } auto origin = DateTime::create(originStr); return TemporalDatum::create(buildProperties(node), origin, calendar); } // --------------------------------------------------------------------------- EngineeringDatumNNPtr WKTParser::Private::buildEngineeringDatum(const WKTNodeNNPtr &node) { return EngineeringDatum::create(buildProperties(node), getAnchor(node)); } // --------------------------------------------------------------------------- ParametricDatumNNPtr WKTParser::Private::buildParametricDatum(const WKTNodeNNPtr &node) { return ParametricDatum::create(buildProperties(node), getAnchor(node)); } // --------------------------------------------------------------------------- CRSNNPtr WKTParser::Private::buildVerticalCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &datumNode = nodeP->lookForChild(WKTConstants::VDATUM, WKTConstants::VERT_DATUM, WKTConstants::VERTICALDATUM, WKTConstants::VRF); auto &ensembleNode = nodeP->lookForChild(WKTConstants::ENSEMBLE); if (isNull(datumNode) && isNull(ensembleNode)) { throw ParsingException("Missing VDATUM or ENSEMBLE node"); } auto &dynamicNode = nodeP->lookForChild(WKTConstants::DYNAMIC); auto datum = !isNull(datumNode) ? buildVerticalReferenceFrame(datumNode, dynamicNode).as_nullable() : nullptr; auto datumEnsemble = !isNull(ensembleNode) ? buildDatumEnsemble(ensembleNode, nullptr, false).as_nullable() : nullptr; auto &csNode = nodeP->lookForChild(WKTConstants::CS_); const auto &nodeValue = nodeP->value(); if (isNull(csNode) && !ci_equal(nodeValue, WKTConstants::VERT_CS) && !ci_equal(nodeValue, WKTConstants::BASEVERTCRS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); auto verticalCS = nn_dynamic_pointer_cast(cs); if (!verticalCS) { ThrowNotExpectedCSType("vertical"); } auto &props = buildProperties(node); // Deal with Lidar WKT1 VertCRS that embeds geoid model in CRS name, // following conventions from // https://pubs.usgs.gov/tm/11b4/pdf/tm11-B4.pdf // page 9 if (ci_equal(nodeValue, WKTConstants::VERT_CS)) { std::string name; if (props.getStringValue(IdentifiedObject::NAME_KEY, name)) { std::string geoidName; for (const char *prefix : {"NAVD88 - ", "NAVD88 via ", "NAVD88 height - ", "NAVD88 height (ftUS) - "}) { if (starts_with(name, prefix)) { geoidName = name.substr(strlen(prefix)); auto pos = geoidName.find_first_of(" ("); if (pos != std::string::npos) { geoidName.resize(pos); } break; } } if (!geoidName.empty()) { const auto &axis = verticalCS->axisList()[0]; const auto &dir = axis->direction(); if (dir == cs::AxisDirection::UP) { if (axis->unit() == common::UnitOfMeasure::METRE) { props.set(IdentifiedObject::NAME_KEY, "NAVD88 height"); props.set(Identifier::CODE_KEY, 5703); props.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } else if (axis->unit().name() == "US survey foot") { props.set(IdentifiedObject::NAME_KEY, "NAVD88 height (ftUS)"); props.set(Identifier::CODE_KEY, 6360); props.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } } PropertyMap propsModel; propsModel.set(IdentifiedObject::NAME_KEY, toupper(geoidName)); PropertyMap propsDatum; propsDatum.set(IdentifiedObject::NAME_KEY, "North American Vertical Datum 1988"); propsDatum.set(Identifier::CODE_KEY, 5103); propsDatum.set(Identifier::CODESPACE_KEY, Identifier::EPSG); datum = VerticalReferenceFrame::create(propsDatum).as_nullable(); const auto dummyCRS = VerticalCRS::create(PropertyMap(), datum, datumEnsemble, NN_NO_CHECK(verticalCS)); const auto model(Transformation::create( propsModel, dummyCRS, dummyCRS, nullptr, OperationMethod::create( PropertyMap(), std::vector()), {}, {})); props.set("GEOID_MODEL", model); } } } auto &geoidModelNode = nodeP->lookForChild(WKTConstants::GEOIDMODEL); if (!isNull(geoidModelNode)) { auto &propsModel = buildProperties(geoidModelNode); const auto dummyCRS = VerticalCRS::create( PropertyMap(), datum, datumEnsemble, NN_NO_CHECK(verticalCS)); const auto model(Transformation::create( propsModel, dummyCRS, dummyCRS, nullptr, OperationMethod::create(PropertyMap(), std::vector()), {}, {})); props.set("GEOID_MODEL", model); } auto crs = nn_static_pointer_cast(VerticalCRS::create( props, datum, datumEnsemble, NN_NO_CHECK(verticalCS))); if (!isNull(datumNode)) { auto &extensionNode = datumNode->lookForChild(WKTConstants::EXTENSION); const auto &extensionChildren = extensionNode->GP()->children(); if (extensionChildren.size() == 2) { if (ci_equal(stripQuotes(extensionChildren[0]), "PROJ4_GRIDS")) { const auto gridName(stripQuotes(extensionChildren[1])); // This is the expansion of EPSG:5703 by old GDAL versions. // See // https://trac.osgeo.org/metacrs/changeset?reponame=&new=2281%40geotiff%2Ftrunk%2Flibgeotiff%2Fcsv%2Fvertcs.override.csv&old=1893%40geotiff%2Ftrunk%2Flibgeotiff%2Fcsv%2Fvertcs.override.csv // It is unlikely that the user really explicitly wants this. if (gridName != "g2003conus.gtx,g2003alaska.gtx," "g2003h01.gtx,g2003p01.gtx" && gridName != "g2012a_conus.gtx,g2012a_alaska.gtx," "g2012a_guam.gtx,g2012a_hawaii.gtx," "g2012a_puertorico.gtx,g2012a_samoa.gtx") { std::string transformationName(crs->nameStr()); if (!ends_with(transformationName, " height")) { transformationName += " height"; } transformationName += " to WGS84 ellipsoidal height"; auto transformation = Transformation:: createGravityRelatedHeightToGeographic3D( PropertyMap().set(IdentifiedObject::NAME_KEY, transformationName), crs, GeographicCRS::EPSG_4979, nullptr, gridName, std::vector()); return nn_static_pointer_cast(BoundCRS::create( crs, GeographicCRS::EPSG_4979, transformation)); } } } } return crs; } // --------------------------------------------------------------------------- DerivedVerticalCRSNNPtr WKTParser::Private::buildDerivedVerticalCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseVertCRSNode = nodeP->lookForChild(WKTConstants::BASEVERTCRS); // given the constraints enforced on calling code path assert(!isNull(baseVertCRSNode)); auto baseVertCRS_tmp = buildVerticalCRS(baseVertCRSNode); auto baseVertCRS = NN_NO_CHECK(baseVertCRS_tmp->extractVerticalCRS()); auto &derivingConversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(derivingConversionNode)) { ThrowMissing(WKTConstants::DERIVINGCONVERSION); } auto derivingConversion = buildConversion( derivingConversionNode, UnitOfMeasure::NONE, UnitOfMeasure::NONE); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); if (isNull(csNode)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); auto verticalCS = nn_dynamic_pointer_cast(cs); if (!verticalCS) { throw ParsingException( concat("vertical CS expected, but found ", cs->getWKT2Type(true))); } return DerivedVerticalCRS::create(buildProperties(node), baseVertCRS, derivingConversion, NN_NO_CHECK(verticalCS)); } // --------------------------------------------------------------------------- CompoundCRSNNPtr WKTParser::Private::buildCompoundCRS(const WKTNodeNNPtr &node) { std::vector components; for (const auto &child : node->GP()->children()) { auto crs = buildCRS(child); if (crs) { components.push_back(NN_NO_CHECK(crs)); } } return CompoundCRS::create(buildProperties(node), components); } // --------------------------------------------------------------------------- BoundCRSNNPtr WKTParser::Private::buildBoundCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &abridgedNode = nodeP->lookForChild(WKTConstants::ABRIDGEDTRANSFORMATION); if (isNull(abridgedNode)) { ThrowNotEnoughChildren(WKTConstants::ABRIDGEDTRANSFORMATION); } auto &methodNode = abridgedNode->GP()->lookForChild(WKTConstants::METHOD); if (isNull(methodNode)) { ThrowMissing(WKTConstants::METHOD); } if (methodNode->GP()->childrenSize() == 0) { ThrowNotEnoughChildren(WKTConstants::METHOD); } auto &sourceCRSNode = nodeP->lookForChild(WKTConstants::SOURCECRS); const auto &sourceCRSNodeChildren = sourceCRSNode->GP()->children(); if (sourceCRSNodeChildren.size() != 1) { ThrowNotEnoughChildren(WKTConstants::SOURCECRS); } auto sourceCRS = buildCRS(sourceCRSNodeChildren[0]); if (!sourceCRS) { throw ParsingException("Invalid content in SOURCECRS node"); } auto &targetCRSNode = nodeP->lookForChild(WKTConstants::TARGETCRS); const auto &targetCRSNodeChildren = targetCRSNode->GP()->children(); if (targetCRSNodeChildren.size() != 1) { ThrowNotEnoughChildren(WKTConstants::TARGETCRS); } auto targetCRS = buildCRS(targetCRSNodeChildren[0]); if (!targetCRS) { throw ParsingException("Invalid content in TARGETCRS node"); } std::vector parameters; std::vector values; auto defaultLinearUnit = UnitOfMeasure::NONE; auto defaultAngularUnit = UnitOfMeasure::NONE; consumeParameters(abridgedNode, true, parameters, values, defaultLinearUnit, defaultAngularUnit); CRSPtr sourceTransformationCRS; if (dynamic_cast(targetCRS.get())) { sourceTransformationCRS = sourceCRS->extractGeographicCRS(); if (!sourceTransformationCRS) { sourceTransformationCRS = std::dynamic_pointer_cast(sourceCRS); if (!sourceTransformationCRS) { throw ParsingException( "Cannot find GeographicCRS or VerticalCRS in sourceCRS"); } } } else { sourceTransformationCRS = sourceCRS; } auto transformation = Transformation::create( buildProperties(abridgedNode), NN_NO_CHECK(sourceTransformationCRS), NN_NO_CHECK(targetCRS), nullptr, buildProperties(methodNode), parameters, values, std::vector()); return BoundCRS::create(NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), transformation); } // --------------------------------------------------------------------------- TemporalCSNNPtr WKTParser::Private::buildTemporalCS(const WKTNodeNNPtr &parentNode) { auto &csNode = parentNode->GP()->lookForChild(WKTConstants::CS_); if (isNull(csNode) && !ci_equal(parentNode->GP()->value(), WKTConstants::BASETIMECRS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, parentNode, UnitOfMeasure::NONE); auto temporalCS = nn_dynamic_pointer_cast(cs); if (!temporalCS) { ThrowNotExpectedCSType("temporal"); } return NN_NO_CHECK(temporalCS); } // --------------------------------------------------------------------------- TemporalCRSNNPtr WKTParser::Private::buildTemporalCRS(const WKTNodeNNPtr &node) { auto &datumNode = node->GP()->lookForChild(WKTConstants::TDATUM, WKTConstants::TIMEDATUM); if (isNull(datumNode)) { throw ParsingException("Missing TDATUM / TIMEDATUM node"); } return TemporalCRS::create(buildProperties(node), buildTemporalDatum(datumNode), buildTemporalCS(node)); } // --------------------------------------------------------------------------- DerivedTemporalCRSNNPtr WKTParser::Private::buildDerivedTemporalCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseCRSNode = nodeP->lookForChild(WKTConstants::BASETIMECRS); // given the constraints enforced on calling code path assert(!isNull(baseCRSNode)); auto &derivingConversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(derivingConversionNode)) { ThrowNotEnoughChildren(WKTConstants::DERIVINGCONVERSION); } return DerivedTemporalCRS::create( buildProperties(node), buildTemporalCRS(baseCRSNode), buildConversion(derivingConversionNode, UnitOfMeasure::NONE, UnitOfMeasure::NONE), buildTemporalCS(node)); } // --------------------------------------------------------------------------- EngineeringCRSNNPtr WKTParser::Private::buildEngineeringCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &datumNode = nodeP->lookForChild(WKTConstants::EDATUM, WKTConstants::ENGINEERINGDATUM); if (isNull(datumNode)) { throw ParsingException("Missing EDATUM / ENGINEERINGDATUM node"); } auto &csNode = nodeP->lookForChild(WKTConstants::CS_); if (isNull(csNode) && !ci_equal(nodeP->value(), WKTConstants::BASEENGCRS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); return EngineeringCRS::create(buildProperties(node), buildEngineeringDatum(datumNode), cs); } // --------------------------------------------------------------------------- EngineeringCRSNNPtr WKTParser::Private::buildEngineeringCRSFromLocalCS(const WKTNodeNNPtr &node) { auto &datumNode = node->GP()->lookForChild(WKTConstants::LOCAL_DATUM); auto cs = buildCS(null_node, node, UnitOfMeasure::NONE); auto datum = EngineeringDatum::create( !isNull(datumNode) ? buildProperties(datumNode) : // In theory OGC 01-009 mandates LOCAL_DATUM, but GDAL has a // tradition of emitting just LOCAL_CS["foo"] emptyPropertyMap); return EngineeringCRS::create(buildProperties(node), datum, cs); } // --------------------------------------------------------------------------- DerivedEngineeringCRSNNPtr WKTParser::Private::buildDerivedEngineeringCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseEngCRSNode = nodeP->lookForChild(WKTConstants::BASEENGCRS); // given the constraints enforced on calling code path assert(!isNull(baseEngCRSNode)); auto baseEngCRS = buildEngineeringCRS(baseEngCRSNode); auto &derivingConversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(derivingConversionNode)) { ThrowNotEnoughChildren(WKTConstants::DERIVINGCONVERSION); } auto derivingConversion = buildConversion( derivingConversionNode, UnitOfMeasure::NONE, UnitOfMeasure::NONE); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); if (isNull(csNode)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); return DerivedEngineeringCRS::create(buildProperties(node), baseEngCRS, derivingConversion, cs); } // --------------------------------------------------------------------------- ParametricCSNNPtr WKTParser::Private::buildParametricCS(const WKTNodeNNPtr &parentNode) { auto &csNode = parentNode->GP()->lookForChild(WKTConstants::CS_); if (isNull(csNode) && !ci_equal(parentNode->GP()->value(), WKTConstants::BASEPARAMCRS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, parentNode, UnitOfMeasure::NONE); auto parametricCS = nn_dynamic_pointer_cast(cs); if (!parametricCS) { ThrowNotExpectedCSType("parametric"); } return NN_NO_CHECK(parametricCS); } // --------------------------------------------------------------------------- ParametricCRSNNPtr WKTParser::Private::buildParametricCRS(const WKTNodeNNPtr &node) { auto &datumNode = node->GP()->lookForChild(WKTConstants::PDATUM, WKTConstants::PARAMETRICDATUM); if (isNull(datumNode)) { throw ParsingException("Missing PDATUM / PARAMETRICDATUM node"); } return ParametricCRS::create(buildProperties(node), buildParametricDatum(datumNode), buildParametricCS(node)); } // --------------------------------------------------------------------------- DerivedParametricCRSNNPtr WKTParser::Private::buildDerivedParametricCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseParamCRSNode = nodeP->lookForChild(WKTConstants::BASEPARAMCRS); // given the constraints enforced on calling code path assert(!isNull(baseParamCRSNode)); auto &derivingConversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(derivingConversionNode)) { ThrowNotEnoughChildren(WKTConstants::DERIVINGCONVERSION); } return DerivedParametricCRS::create( buildProperties(node), buildParametricCRS(baseParamCRSNode), buildConversion(derivingConversionNode, UnitOfMeasure::NONE, UnitOfMeasure::NONE), buildParametricCS(node)); } // --------------------------------------------------------------------------- DerivedProjectedCRSNNPtr WKTParser::Private::buildDerivedProjectedCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); auto &baseProjCRSNode = nodeP->lookForChild(WKTConstants::BASEPROJCRS); if (isNull(baseProjCRSNode)) { ThrowNotEnoughChildren(WKTConstants::BASEPROJCRS); } auto baseProjCRS = buildProjectedCRS(baseProjCRSNode); auto &conversionNode = nodeP->lookForChild(WKTConstants::DERIVINGCONVERSION); if (isNull(conversionNode)) { ThrowNotEnoughChildren(WKTConstants::DERIVINGCONVERSION); } auto linearUnit = buildUnitInSubNode(node); auto angularUnit = baseProjCRS->baseCRS()->coordinateSystem()->axisList()[0]->unit(); auto conversion = buildConversion(conversionNode, linearUnit, angularUnit); auto &csNode = nodeP->lookForChild(WKTConstants::CS_); if (isNull(csNode) && !ci_equal(nodeP->value(), WKTConstants::PROJCS)) { ThrowMissing(WKTConstants::CS_); } auto cs = buildCS(csNode, node, UnitOfMeasure::NONE); return DerivedProjectedCRS::create(buildProperties(node), baseProjCRS, conversion, cs); } // --------------------------------------------------------------------------- static bool isGeodeticCRS(const std::string &name) { return ci_equal(name, WKTConstants::GEODCRS) || // WKT2 ci_equal(name, WKTConstants::GEODETICCRS) || // WKT2 ci_equal(name, WKTConstants::GEOGCRS) || // WKT2 2019 ci_equal(name, WKTConstants::GEOGRAPHICCRS) || // WKT2 2019 ci_equal(name, WKTConstants::GEOGCS) || // WKT1 ci_equal(name, WKTConstants::GEOCCS); // WKT1 } // --------------------------------------------------------------------------- CRSPtr WKTParser::Private::buildCRS(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); const std::string &name(nodeP->value()); if (isGeodeticCRS(name)) { if (!isNull(nodeP->lookForChild(WKTConstants::BASEGEOGCRS, WKTConstants::BASEGEODCRS))) { return buildDerivedGeodeticCRS(node); } else { return util::nn_static_pointer_cast(buildGeodeticCRS(node)); } } if (ci_equal(name, WKTConstants::PROJCS) || ci_equal(name, WKTConstants::PROJCRS) || ci_equal(name, WKTConstants::PROJECTEDCRS)) { return util::nn_static_pointer_cast(buildProjectedCRS(node)); } if (ci_equal(name, WKTConstants::VERT_CS) || ci_equal(name, WKTConstants::VERTCRS) || ci_equal(name, WKTConstants::VERTICALCRS)) { if (!isNull(nodeP->lookForChild(WKTConstants::BASEVERTCRS))) { return util::nn_static_pointer_cast( buildDerivedVerticalCRS(node)); } else { return util::nn_static_pointer_cast(buildVerticalCRS(node)); } } if (ci_equal(name, WKTConstants::COMPD_CS) || ci_equal(name, WKTConstants::COMPOUNDCRS)) { return util::nn_static_pointer_cast(buildCompoundCRS(node)); } if (ci_equal(name, WKTConstants::BOUNDCRS)) { return util::nn_static_pointer_cast(buildBoundCRS(node)); } if (ci_equal(name, WKTConstants::TIMECRS)) { if (!isNull(nodeP->lookForChild(WKTConstants::BASETIMECRS))) { return util::nn_static_pointer_cast( buildDerivedTemporalCRS(node)); } else { return util::nn_static_pointer_cast(buildTemporalCRS(node)); } } if (ci_equal(name, WKTConstants::DERIVEDPROJCRS)) { return util::nn_static_pointer_cast( buildDerivedProjectedCRS(node)); } if (ci_equal(name, WKTConstants::ENGCRS) || ci_equal(name, WKTConstants::ENGINEERINGCRS)) { if (!isNull(nodeP->lookForChild(WKTConstants::BASEENGCRS))) { return util::nn_static_pointer_cast( buildDerivedEngineeringCRS(node)); } else { return util::nn_static_pointer_cast(buildEngineeringCRS(node)); } } if (ci_equal(name, WKTConstants::LOCAL_CS)) { return util::nn_static_pointer_cast( buildEngineeringCRSFromLocalCS(node)); } if (ci_equal(name, WKTConstants::PARAMETRICCRS)) { if (!isNull(nodeP->lookForChild(WKTConstants::BASEPARAMCRS))) { return util::nn_static_pointer_cast( buildDerivedParametricCRS(node)); } else { return util::nn_static_pointer_cast(buildParametricCRS(node)); } } return nullptr; } // --------------------------------------------------------------------------- BaseObjectNNPtr WKTParser::Private::build(const WKTNodeNNPtr &node) { const auto *nodeP = node->GP(); const std::string &name(nodeP->value()); auto crs = buildCRS(node); if (crs) { if (!toWGS84Parameters_.empty()) { return util::nn_static_pointer_cast( BoundCRS::createFromTOWGS84(NN_NO_CHECK(crs), toWGS84Parameters_)); } if (!datumPROJ4Grids_.empty()) { return util::nn_static_pointer_cast( BoundCRS::createFromNadgrids(NN_NO_CHECK(crs), datumPROJ4Grids_)); } return util::nn_static_pointer_cast(NN_NO_CHECK(crs)); } if (ci_equal(name, WKTConstants::DATUM) || ci_equal(name, WKTConstants::GEODETICDATUM) || ci_equal(name, WKTConstants::TRF)) { return util::nn_static_pointer_cast( buildGeodeticReferenceFrame(node, PrimeMeridian::GREENWICH, null_node)); } if (ci_equal(name, WKTConstants::ENSEMBLE)) { return util::nn_static_pointer_cast(buildDatumEnsemble( node, PrimeMeridian::GREENWICH, !isNull(nodeP->lookForChild(WKTConstants::ELLIPSOID)))); } if (ci_equal(name, WKTConstants::VDATUM) || ci_equal(name, WKTConstants::VERT_DATUM) || ci_equal(name, WKTConstants::VERTICALDATUM) || ci_equal(name, WKTConstants::VRF)) { return util::nn_static_pointer_cast( buildVerticalReferenceFrame(node, null_node)); } if (ci_equal(name, WKTConstants::TDATUM) || ci_equal(name, WKTConstants::TIMEDATUM)) { return util::nn_static_pointer_cast( buildTemporalDatum(node)); } if (ci_equal(name, WKTConstants::EDATUM) || ci_equal(name, WKTConstants::ENGINEERINGDATUM)) { return util::nn_static_pointer_cast( buildEngineeringDatum(node)); } if (ci_equal(name, WKTConstants::PDATUM) || ci_equal(name, WKTConstants::PARAMETRICDATUM)) { return util::nn_static_pointer_cast( buildParametricDatum(node)); } if (ci_equal(name, WKTConstants::ELLIPSOID) || ci_equal(name, WKTConstants::SPHEROID)) { return util::nn_static_pointer_cast(buildEllipsoid(node)); } if (ci_equal(name, WKTConstants::COORDINATEOPERATION)) { auto transf = buildCoordinateOperation(node); const char *prefixes[] = { "PROJ-based operation method: ", "PROJ-based operation method (approximate): "}; for (const char *prefix : prefixes) { if (starts_with(transf->method()->nameStr(), prefix)) { auto projString = transf->method()->nameStr().substr(strlen(prefix)); return util::nn_static_pointer_cast( PROJBasedOperation::create( PropertyMap(), projString, transf->sourceCRS(), transf->targetCRS(), transf->coordinateOperationAccuracies())); } } return util::nn_static_pointer_cast(transf); } if (ci_equal(name, WKTConstants::CONVERSION)) { auto conv = buildConversion(node, UnitOfMeasure::METRE, UnitOfMeasure::DEGREE); if (starts_with(conv->method()->nameStr(), "PROJ-based operation method: ")) { auto projString = conv->method()->nameStr().substr( strlen("PROJ-based operation method: ")); return util::nn_static_pointer_cast( PROJBasedOperation::create(PropertyMap(), projString, nullptr, nullptr, {})); } return util::nn_static_pointer_cast(conv); } if (ci_equal(name, WKTConstants::CONCATENATEDOPERATION)) { return util::nn_static_pointer_cast( buildConcatenatedOperation(node)); } if (ci_equal(name, WKTConstants::ID) || ci_equal(name, WKTConstants::AUTHORITY)) { return util::nn_static_pointer_cast( NN_NO_CHECK(buildId(node, false, false))); } throw ParsingException(concat("unhandled keyword: ", name)); } // --------------------------------------------------------------------------- class JSONParser { DatabaseContextPtr dbContext_{}; static std::string getString(const json &j, const char *key); static json getObject(const json &j, const char *key); static json getArray(const json &j, const char *key); static double getNumber(const json &j, const char *key); static UnitOfMeasure getUnit(const json &j, const char *key); static std::string getName(const json &j); static std::string getType(const json &j); static Length getLength(const json &j, const char *key); static Measure getMeasure(const json &j); IdentifierNNPtr buildId(const json &j, bool removeInverseOf); ObjectDomainPtr buildObjectDomain(const json &j); PropertyMap buildProperties(const json &j, bool removeInverseOf = false); GeographicCRSNNPtr buildGeographicCRS(const json &j); GeodeticCRSNNPtr buildGeodeticCRS(const json &j); ProjectedCRSNNPtr buildProjectedCRS(const json &j); ConversionNNPtr buildConversion(const json &j); DatumEnsembleNNPtr buildDatumEnsemble(const json &j); GeodeticReferenceFrameNNPtr buildGeodeticReferenceFrame(const json &j); VerticalReferenceFrameNNPtr buildVerticalReferenceFrame(const json &j); DynamicGeodeticReferenceFrameNNPtr buildDynamicGeodeticReferenceFrame(const json &j); DynamicVerticalReferenceFrameNNPtr buildDynamicVerticalReferenceFrame(const json &j); EllipsoidNNPtr buildEllipsoid(const json &j); PrimeMeridianNNPtr buildPrimeMeridian(const json &j); CoordinateSystemNNPtr buildCS(const json &j); CoordinateSystemAxisNNPtr buildAxis(const json &j); VerticalCRSNNPtr buildVerticalCRS(const json &j); CRSNNPtr buildCRS(const json &j); CompoundCRSNNPtr buildCompoundCRS(const json &j); BoundCRSNNPtr buildBoundCRS(const json &j); TransformationNNPtr buildTransformation(const json &j); ConcatenatedOperationNNPtr buildConcatenatedOperation(const json &j); static util::optional getAnchor(const json &j) { util::optional anchor; if (j.contains("anchor")) { anchor = getString(j, "anchor"); } return anchor; } EngineeringDatumNNPtr buildEngineeringDatum(const json &j) { return EngineeringDatum::create(buildProperties(j), getAnchor(j)); } ParametricDatumNNPtr buildParametricDatum(const json &j) { return ParametricDatum::create(buildProperties(j), getAnchor(j)); } TemporalDatumNNPtr buildTemporalDatum(const json &j) { auto calendar = getString(j, "calendar"); auto origin = DateTime::create(j.contains("time_origin") ? getString(j, "time_origin") : std::string()); return TemporalDatum::create(buildProperties(j), origin, calendar); } template util::nn> buildCRS(const json &j, DatumBuilderType f) { auto datum = (this->*f)(getObject(j, "datum")); auto cs = buildCS(getObject(j, "coordinate_system")); auto csCast = util::nn_dynamic_pointer_cast(cs); if (!csCast) { throw ParsingException("coordinate_system not of expected type"); } return TargetCRS::create(buildProperties(j), datum, NN_NO_CHECK(csCast)); } template util::nn> buildDerivedCRS(const json &j) { auto baseCRSObj = create(getObject(j, "base_crs")); auto baseCRS = util::nn_dynamic_pointer_cast(baseCRSObj); if (!baseCRS) { throw ParsingException("base_crs not of expected type"); } auto cs = buildCS(getObject(j, "coordinate_system")); auto csCast = util::nn_dynamic_pointer_cast(cs); if (!csCast) { throw ParsingException("coordinate_system not of expected type"); } auto conv = buildConversion(getObject(j, "conversion")); return TargetCRS::create(buildProperties(j), NN_NO_CHECK(baseCRS), conv, NN_NO_CHECK(csCast)); } public: JSONParser() = default; JSONParser &attachDatabaseContext(const DatabaseContextPtr &dbContext) { dbContext_ = dbContext; return *this; } BaseObjectNNPtr create(const json &j); }; // --------------------------------------------------------------------------- std::string JSONParser::getString(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (!v.is_string()) { throw ParsingException(std::string("The value of \"") + key + "\" should be a string"); } return v.get(); } // --------------------------------------------------------------------------- json JSONParser::getObject(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (!v.is_object()) { throw ParsingException(std::string("The value of \"") + key + "\" should be a object"); } return v.get(); } // --------------------------------------------------------------------------- json JSONParser::getArray(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (!v.is_array()) { throw ParsingException(std::string("The value of \"") + key + "\" should be a array"); } return v.get(); } // --------------------------------------------------------------------------- double JSONParser::getNumber(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (!v.is_number()) { throw ParsingException(std::string("The value of \"") + key + "\" should be a number"); } return v.get(); } // --------------------------------------------------------------------------- UnitOfMeasure JSONParser::getUnit(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (v.is_string()) { auto vStr = v.get(); for (const auto &unit : {UnitOfMeasure::METRE, UnitOfMeasure::DEGREE, UnitOfMeasure::SCALE_UNITY}) { if (vStr == unit.name()) return unit; } throw ParsingException("Unknown unit name: " + vStr); } if (!v.is_object()) { throw ParsingException(std::string("The value of \"") + key + "\" should be a string or an object"); } auto typeStr = getType(v); UnitOfMeasure::Type type = UnitOfMeasure::Type::UNKNOWN; if (typeStr == "LinearUnit") { type = UnitOfMeasure::Type::LINEAR; } else if (typeStr == "AngularUnit") { type = UnitOfMeasure::Type::ANGULAR; } else if (typeStr == "ScaleUnit") { type = UnitOfMeasure::Type::SCALE; } else if (typeStr == "TimeUnit") { type = UnitOfMeasure::Type::TIME; } else if (typeStr == "ParametricUnit") { type = UnitOfMeasure::Type::PARAMETRIC; } else if (typeStr == "Unit") { type = UnitOfMeasure::Type::UNKNOWN; } else { throw ParsingException("Unsupported value of \"type\""); } auto nameStr = getName(v); auto convFactor = getNumber(v, "conversion_factor"); std::string authorityStr; std::string codeStr; if (v.contains("authority") && v.contains("code")) { authorityStr = getString(v, "authority"); auto code = v["code"]; if (code.is_string()) { codeStr = code.get(); } else if (code.is_number_integer()) { codeStr = internal::toString(code.get()); } else { throw ParsingException("Unexpected type for value of \"code\""); } } return UnitOfMeasure(nameStr, convFactor, type, authorityStr, codeStr); } // --------------------------------------------------------------------------- std::string JSONParser::getName(const json &j) { return getString(j, "name"); } // --------------------------------------------------------------------------- std::string JSONParser::getType(const json &j) { return getString(j, "type"); } // --------------------------------------------------------------------------- Length JSONParser::getLength(const json &j, const char *key) { if (!j.contains(key)) { throw ParsingException(std::string("Missing \"") + key + "\" key"); } auto v = j[key]; if (v.is_number()) { return Length(v.get(), UnitOfMeasure::METRE); } if (v.is_object()) { return Length(getMeasure(v)); } throw ParsingException(std::string("The value of \"") + key + "\" should be a number or an object"); } // --------------------------------------------------------------------------- Measure JSONParser::getMeasure(const json &j) { return Measure(getNumber(j, "value"), getUnit(j, "unit")); } // --------------------------------------------------------------------------- ObjectDomainPtr JSONParser::buildObjectDomain(const json &j) { optional scope; if (j.contains("scope")) { scope = getString(j, "scope"); } std::string area; if (j.contains("area")) { area = getString(j, "area"); } std::vector geogExtent; if (j.contains("bbox")) { auto bbox = getObject(j, "bbox"); double south = getNumber(bbox, "south_latitude"); double west = getNumber(bbox, "west_longitude"); double north = getNumber(bbox, "north_latitude"); double east = getNumber(bbox, "east_longitude"); geogExtent.emplace_back( GeographicBoundingBox::create(west, south, east, north)); } if (scope.has_value() || !area.empty() || !geogExtent.empty()) { util::optional description; if (!area.empty()) description = area; ExtentPtr extent; if (description.has_value() || !geogExtent.empty()) { extent = Extent::create(description, geogExtent, {}, {}).as_nullable(); } return ObjectDomain::create(scope, extent).as_nullable(); } return nullptr; } // --------------------------------------------------------------------------- IdentifierNNPtr JSONParser::buildId(const json &j, bool removeInverseOf) { PropertyMap propertiesId; auto codeSpace(getString(j, "authority")); if (removeInverseOf && starts_with(codeSpace, "INVERSE(") && codeSpace.back() == ')') { codeSpace = codeSpace.substr(strlen("INVERSE(")); codeSpace.resize(codeSpace.size() - 1); } propertiesId.set(metadata::Identifier::CODESPACE_KEY, codeSpace); propertiesId.set(metadata::Identifier::AUTHORITY_KEY, codeSpace); if (!j.contains("code")) { throw ParsingException("Missing \"code\" key"); } std::string code; auto codeJ = j["code"]; if (codeJ.is_string()) { code = codeJ.get(); } else if (codeJ.is_number_integer()) { code = internal::toString(codeJ.get()); } else { throw ParsingException("Unexpected type for value of \"code\""); } return Identifier::create(code, propertiesId); } // --------------------------------------------------------------------------- PropertyMap JSONParser::buildProperties(const json &j, bool removeInverseOf) { PropertyMap map; std::string name(getName(j)); if (removeInverseOf && starts_with(name, "Inverse of ")) { name = name.substr(strlen("Inverse of ")); } map.set(IdentifiedObject::NAME_KEY, name); if (j.contains("ids")) { auto idsJ = getArray(j, "ids"); auto identifiers = ArrayOfBaseObject::create(); for (const auto &idJ : idsJ) { if (!idJ.is_object()) { throw ParsingException( "Unexpected type for value of \"ids\" child"); } identifiers->add(buildId(idJ, removeInverseOf)); } map.set(IdentifiedObject::IDENTIFIERS_KEY, identifiers); } else if (j.contains("id")) { auto idJ = getObject(j, "id"); auto identifiers = ArrayOfBaseObject::create(); identifiers->add(buildId(idJ, removeInverseOf)); map.set(IdentifiedObject::IDENTIFIERS_KEY, identifiers); } if (j.contains("remarks")) { map.set(IdentifiedObject::REMARKS_KEY, getString(j, "remarks")); } if (j.contains("usages")) { ArrayOfBaseObjectNNPtr array = ArrayOfBaseObject::create(); auto usages = j["usages"]; if (!usages.is_array()) { throw ParsingException("Unexpected type for value of \"usages\""); } for (const auto &usage : usages) { if (!usage.is_object()) { throw ParsingException( "Unexpected type for value of \"usages\" child"); } auto objectDomain = buildObjectDomain(usage); if (!objectDomain) { throw ParsingException("missing children in \"usages\" child"); } array->add(NN_NO_CHECK(objectDomain)); } if (!array->empty()) { map.set(ObjectUsage::OBJECT_DOMAIN_KEY, array); } } else { auto objectDomain = buildObjectDomain(j); if (objectDomain) { map.set(ObjectUsage::OBJECT_DOMAIN_KEY, NN_NO_CHECK(objectDomain)); } } return map; } // --------------------------------------------------------------------------- BaseObjectNNPtr JSONParser::create(const json &j) { if (!j.is_object()) { throw ParsingException("JSON object expected"); } auto type = getString(j, "type"); if (type == "GeographicCRS") { return buildGeographicCRS(j); } if (type == "GeodeticCRS") { return buildGeodeticCRS(j); } if (type == "ProjectedCRS") { return buildProjectedCRS(j); } if (type == "VerticalCRS") { return buildVerticalCRS(j); } if (type == "CompoundCRS") { return buildCompoundCRS(j); } if (type == "BoundCRS") { return buildBoundCRS(j); } if (type == "EngineeringCRS") { return buildCRS(j, &JSONParser::buildEngineeringDatum); } if (type == "ParametricCRS") { return buildCRS(j, &JSONParser::buildParametricDatum); } if (type == "TemporalCRS") { return buildCRS(j, &JSONParser::buildTemporalDatum); } if (type == "DerivedGeodeticCRS") { auto baseCRSObj = create(getObject(j, "base_crs")); auto baseCRS = util::nn_dynamic_pointer_cast(baseCRSObj); if (!baseCRS) { throw ParsingException("base_crs not of expected type"); } auto cs = buildCS(getObject(j, "coordinate_system")); auto conv = buildConversion(getObject(j, "conversion")); auto csCartesian = util::nn_dynamic_pointer_cast(cs); if (csCartesian) return DerivedGeodeticCRS::create(buildProperties(j), NN_NO_CHECK(baseCRS), conv, NN_NO_CHECK(csCartesian)); auto csSpherical = util::nn_dynamic_pointer_cast(cs); if (csSpherical) return DerivedGeodeticCRS::create(buildProperties(j), NN_NO_CHECK(baseCRS), conv, NN_NO_CHECK(csSpherical)); throw ParsingException("coordinate_system not of expected type"); } if (type == "DerivedGeographicCRS") { return buildDerivedCRS(j); } if (type == "DerivedProjectedCRS") { return buildDerivedCRS(j); } if (type == "DerivedVerticalCRS") { return buildDerivedCRS(j); } if (type == "DerivedEngineeringCRS") { return buildDerivedCRS(j); } if (type == "DerivedParametricCRS") { return buildDerivedCRS(j); } if (type == "DerivedTemporalCRS") { return buildDerivedCRS(j); } if (type == "DatumEnsemble") { return buildDatumEnsemble(j); } if (type == "GeodeticReferenceFrame") { return buildGeodeticReferenceFrame(j); } if (type == "VerticalReferenceFrame") { return buildVerticalReferenceFrame(j); } if (type == "DynamicGeodeticReferenceFrame") { return buildDynamicGeodeticReferenceFrame(j); } if (type == "DynamicVerticalReferenceFrame") { return buildDynamicVerticalReferenceFrame(j); } if (type == "EngineeringDatum") { return buildEngineeringDatum(j); } if (type == "ParametricDatum") { return buildParametricDatum(j); } if (type == "TemporalDatum") { return buildTemporalDatum(j); } if (type == "Ellipsoid") { return buildEllipsoid(j); } if (type == "PrimeMeridian") { return buildPrimeMeridian(j); } if (type == "CoordinateSystem") { return buildCS(j); } if (type == "Conversion") { return buildConversion(j); } if (type == "Transformation") { return buildTransformation(j); } if (type == "ConcatenatedOperation") { return buildConcatenatedOperation(j); } throw ParsingException("Unsupported value of \"type\""); } // --------------------------------------------------------------------------- GeographicCRSNNPtr JSONParser::buildGeographicCRS(const json &j) { GeodeticReferenceFramePtr datum; DatumEnsemblePtr datumEnsemble; if (j.contains("datum")) { auto datumJ = getObject(j, "datum"); datum = util::nn_dynamic_pointer_cast( create(datumJ)); if (!datum) { throw ParsingException("datum of wrong type"); } } else { datumEnsemble = buildDatumEnsemble(getObject(j, "datum_ensemble")).as_nullable(); } auto csJ = getObject(j, "coordinate_system"); auto ellipsoidalCS = util::nn_dynamic_pointer_cast(buildCS(csJ)); if (!ellipsoidalCS) { throw ParsingException("expected an ellipsoidal CS"); } return GeographicCRS::create(buildProperties(j), datum, datumEnsemble, NN_NO_CHECK(ellipsoidalCS)); } // --------------------------------------------------------------------------- GeodeticCRSNNPtr JSONParser::buildGeodeticCRS(const json &j) { auto datumJ = getObject(j, "datum"); if (getType(datumJ) != "GeodeticReferenceFrame") { throw ParsingException("Unsupported type for datum."); } auto datum = buildGeodeticReferenceFrame(datumJ); DatumEnsemblePtr datumEnsemble; auto csJ = getObject(j, "coordinate_system"); auto cs = buildCS(csJ); auto props = buildProperties(j); auto cartesianCS = nn_dynamic_pointer_cast(cs); if (cartesianCS) { if (cartesianCS->axisList().size() != 3) { throw ParsingException( "Cartesian CS for a GeodeticCRS should have 3 axis"); } try { return GeodeticCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(cartesianCS)); } catch (const util::Exception &e) { throw ParsingException(std::string("buildGeodeticCRS: ") + e.what()); } } auto sphericalCS = nn_dynamic_pointer_cast(cs); if (sphericalCS) { try { return GeodeticCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(sphericalCS)); } catch (const util::Exception &e) { throw ParsingException(std::string("buildGeodeticCRS: ") + e.what()); } } throw ParsingException("expected a Cartesian or spherical CS"); } // --------------------------------------------------------------------------- ProjectedCRSNNPtr JSONParser::buildProjectedCRS(const json &j) { auto baseCRS = buildGeographicCRS(getObject(j, "base_crs")); auto csJ = getObject(j, "coordinate_system"); auto cartesianCS = util::nn_dynamic_pointer_cast(buildCS(csJ)); if (!cartesianCS) { throw ParsingException("expected a Cartesian CS"); } auto conv = buildConversion(getObject(j, "conversion")); return ProjectedCRS::create(buildProperties(j), baseCRS, conv, NN_NO_CHECK(cartesianCS)); } // --------------------------------------------------------------------------- VerticalCRSNNPtr JSONParser::buildVerticalCRS(const json &j) { VerticalReferenceFramePtr datum; DatumEnsemblePtr datumEnsemble; if (j.contains("datum")) { auto datumJ = getObject(j, "datum"); datum = util::nn_dynamic_pointer_cast( create(datumJ)); if (!datum) { throw ParsingException("datum of wrong type"); } } else { datumEnsemble = buildDatumEnsemble(getObject(j, "datum_ensemble")).as_nullable(); } auto csJ = getObject(j, "coordinate_system"); auto verticalCS = util::nn_dynamic_pointer_cast(buildCS(csJ)); if (!verticalCS) { throw ParsingException("expected a vertical CS"); } auto props = buildProperties(j); if (j.contains("geoid_model")) { auto geoidModelJ = getObject(j, "geoid_model"); auto propsModel = buildProperties(geoidModelJ); const auto dummyCRS = VerticalCRS::create( PropertyMap(), datum, datumEnsemble, NN_NO_CHECK(verticalCS)); CRSPtr interpolationCRS; if (geoidModelJ.contains("interpolation_crs")) { auto interpolationCRSJ = getObject(geoidModelJ, "interpolation_crs"); interpolationCRS = buildCRS(interpolationCRSJ).as_nullable(); } const auto model(Transformation::create( propsModel, dummyCRS, GeographicCRS::EPSG_4979, // arbitrarily chosen. Ignored, interpolationCRS, OperationMethod::create(PropertyMap(), std::vector()), {}, {})); props.set("GEOID_MODEL", model); } return VerticalCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(verticalCS)); } // --------------------------------------------------------------------------- CRSNNPtr JSONParser::buildCRS(const json &j) { auto crs = util::nn_dynamic_pointer_cast(create(j)); if (crs) { return NN_NO_CHECK(crs); } throw ParsingException("Object is not a CRS"); } // --------------------------------------------------------------------------- CompoundCRSNNPtr JSONParser::buildCompoundCRS(const json &j) { auto componentsJ = getArray(j, "components"); std::vector components; for (const auto &componentJ : componentsJ) { if (!componentJ.is_object()) { throw ParsingException( "Unexpected type for a \"components\" child"); } components.push_back(buildCRS(componentJ)); } return CompoundCRS::create(buildProperties(j), components); } // --------------------------------------------------------------------------- ConversionNNPtr JSONParser::buildConversion(const json &j) { auto methodJ = getObject(j, "method"); auto convProps = buildProperties(j); auto methodProps = buildProperties(methodJ); if (!j.contains("parameters")) { return Conversion::create(convProps, methodProps, {}, {}); } auto parametersJ = getArray(j, "parameters"); std::vector parameters; std::vector values; for (const auto ¶m : parametersJ) { if (!param.is_object()) { throw ParsingException( "Unexpected type for a \"parameters\" child"); } parameters.emplace_back( OperationParameter::create(buildProperties(param))); values.emplace_back(ParameterValue::create(getMeasure(param))); } std::string convName; std::string methodName; if (convProps.getStringValue(IdentifiedObject::NAME_KEY, convName) && methodProps.getStringValue(IdentifiedObject::NAME_KEY, methodName) && starts_with(convName, "Inverse of ") && starts_with(methodName, "Inverse of ")) { auto invConvProps = buildProperties(j, true); auto invMethodProps = buildProperties(methodJ, true); return NN_NO_CHECK(util::nn_dynamic_pointer_cast( Conversion::create(invConvProps, invMethodProps, parameters, values) ->inverse())); } return Conversion::create(convProps, methodProps, parameters, values); } // --------------------------------------------------------------------------- BoundCRSNNPtr JSONParser::buildBoundCRS(const json &j) { auto sourceCRS = buildCRS(getObject(j, "source_crs")); auto targetCRS = buildCRS(getObject(j, "target_crs")); auto transformationJ = getObject(j, "transformation"); auto methodJ = getObject(transformationJ, "method"); auto parametersJ = getArray(transformationJ, "parameters"); std::vector parameters; std::vector values; for (const auto ¶m : parametersJ) { if (!param.is_object()) { throw ParsingException( "Unexpected type for a \"parameters\" child"); } parameters.emplace_back( OperationParameter::create(buildProperties(param))); if (param.contains("value")) { auto v = param["value"]; if (v.is_string()) { values.emplace_back( ParameterValue::createFilename(v.get())); continue; } } values.emplace_back(ParameterValue::create(getMeasure(param))); } CRSPtr sourceTransformationCRS; if (dynamic_cast(targetCRS.get())) { sourceTransformationCRS = sourceCRS->extractGeographicCRS(); if (!sourceTransformationCRS) { sourceTransformationCRS = std::dynamic_pointer_cast(sourceCRS.as_nullable()); if (!sourceTransformationCRS) { throw ParsingException( "Cannot find GeographicCRS or VerticalCRS in sourceCRS"); } } } else { sourceTransformationCRS = sourceCRS; } auto transformation = Transformation::create( buildProperties(transformationJ), NN_NO_CHECK(sourceTransformationCRS), targetCRS, nullptr, buildProperties(methodJ), parameters, values, std::vector()); return BoundCRS::create(sourceCRS, targetCRS, transformation); } // --------------------------------------------------------------------------- TransformationNNPtr JSONParser::buildTransformation(const json &j) { auto sourceCRS = buildCRS(getObject(j, "source_crs")); auto targetCRS = buildCRS(getObject(j, "target_crs")); auto methodJ = getObject(j, "method"); auto parametersJ = getArray(j, "parameters"); std::vector parameters; std::vector values; for (const auto ¶m : parametersJ) { if (!param.is_object()) { throw ParsingException( "Unexpected type for a \"parameters\" child"); } parameters.emplace_back( OperationParameter::create(buildProperties(param))); if (param.contains("value")) { auto v = param["value"]; if (v.is_string()) { values.emplace_back( ParameterValue::createFilename(v.get())); continue; } } values.emplace_back(ParameterValue::create(getMeasure(param))); } CRSPtr interpolationCRS; if (j.contains("interpolation_crs")) { interpolationCRS = buildCRS(getObject(j, "interpolation_crs")).as_nullable(); } std::vector accuracies; if (j.contains("accuracy")) { accuracies.push_back( PositionalAccuracy::create(getString(j, "accuracy"))); } return Transformation::create(buildProperties(j), sourceCRS, targetCRS, interpolationCRS, buildProperties(methodJ), parameters, values, accuracies); } // --------------------------------------------------------------------------- ConcatenatedOperationNNPtr JSONParser::buildConcatenatedOperation(const json &j) { auto sourceCRS = buildCRS(getObject(j, "source_crs")); auto targetCRS = buildCRS(getObject(j, "target_crs")); auto stepsJ = getArray(j, "steps"); std::vector operations; for (const auto &stepJ : stepsJ) { if (!stepJ.is_object()) { throw ParsingException("Unexpected type for a \"steps\" child"); } auto op = nn_dynamic_pointer_cast(create(stepJ)); if (!op) { throw ParsingException("Invalid content in a \"steps\" child"); } operations.emplace_back(NN_NO_CHECK(op)); } ConcatenatedOperation::fixStepsDirection(sourceCRS, targetCRS, operations); try { return ConcatenatedOperation::create( buildProperties(j), operations, std::vector()); } catch (const InvalidOperation &e) { throw ParsingException( std::string("Cannot build concatenated operation: ") + e.what()); } } // --------------------------------------------------------------------------- CoordinateSystemAxisNNPtr JSONParser::buildAxis(const json &j) { auto dirString = getString(j, "direction"); auto abbreviation = getString(j, "abbreviation"); auto unit = j.contains("unit") ? getUnit(j, "unit") : UnitOfMeasure(std::string(), 1.0, UnitOfMeasure::Type::NONE); auto direction = AxisDirection::valueOf(dirString); if (!direction) { throw ParsingException(concat("unhandled axis direction: ", dirString)); } return CoordinateSystemAxis::create(buildProperties(j), abbreviation, *direction, unit, nullptr /* meridian */); } // --------------------------------------------------------------------------- CoordinateSystemNNPtr JSONParser::buildCS(const json &j) { auto subtype = getString(j, "subtype"); if (!j.contains("axis")) { throw ParsingException("Missing \"axis\" key"); } auto jAxisList = j["axis"]; if (!jAxisList.is_array()) { throw ParsingException("Unexpected type for value of \"axis\""); } std::vector axisList; for (const auto &axis : jAxisList) { if (!axis.is_object()) { throw ParsingException( "Unexpected type for value of a \"axis\" member"); } axisList.emplace_back(buildAxis(axis)); } const PropertyMap &csMap = emptyPropertyMap; if (subtype == "ellipsoidal") { if (axisList.size() == 2) { return EllipsoidalCS::create(csMap, axisList[0], axisList[1]); } if (axisList.size() == 3) { return EllipsoidalCS::create(csMap, axisList[0], axisList[1], axisList[2]); } throw ParsingException("Expected 2 or 3 axis"); } if (subtype == "Cartesian") { if (axisList.size() == 2) { return CartesianCS::create(csMap, axisList[0], axisList[1]); } if (axisList.size() == 3) { return CartesianCS::create(csMap, axisList[0], axisList[1], axisList[2]); } throw ParsingException("Expected 2 or 3 axis"); } if (subtype == "vertical") { if (axisList.size() == 1) { return VerticalCS::create(csMap, axisList[0]); } throw ParsingException("Expected 1 axis"); } if (subtype == "spherical") { if (axisList.size() == 3) { return SphericalCS::create(csMap, axisList[0], axisList[1], axisList[2]); } throw ParsingException("Expected 3 axis"); } if (subtype == "ordinal") { return OrdinalCS::create(csMap, axisList); } if (subtype == "parametric") { if (axisList.size() == 1) { return ParametricCS::create(csMap, axisList[0]); } throw ParsingException("Expected 1 axis"); } if (subtype == "TemporalDateTime") { if (axisList.size() == 1) { return DateTimeTemporalCS::create(csMap, axisList[0]); } throw ParsingException("Expected 1 axis"); } if (subtype == "TemporalCount") { if (axisList.size() == 1) { return TemporalCountCS::create(csMap, axisList[0]); } throw ParsingException("Expected 1 axis"); } if (subtype == "TemporalMeasure") { if (axisList.size() == 1) { return TemporalMeasureCS::create(csMap, axisList[0]); } throw ParsingException("Expected 1 axis"); } throw ParsingException("Unhandled value for subtype"); } // --------------------------------------------------------------------------- DatumEnsembleNNPtr JSONParser::buildDatumEnsemble(const json &j) { auto membersJ = getArray(j, "members"); std::vector datums; const bool hasEllipsoid(j.contains("ellipsoid")); for (const auto &memberJ : membersJ) { if (!memberJ.is_object()) { throw ParsingException( "Unexpected type for value of a \"members\" member"); } auto datumName(getName(memberJ)); if (dbContext_ && memberJ.contains("id")) { auto id = getObject(memberJ, "id"); auto authority = getString(id, "authority"); auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), authority); auto code = id["code"]; std::string codeStr; if (code.is_string()) { codeStr = code.get(); } else if (code.is_number_integer()) { codeStr = internal::toString(code.get()); } else { throw ParsingException("Unexpected type for value of \"code\""); } try { datums.push_back(authFactory->createDatum(codeStr)); } catch (const std::exception &) { throw ParsingException("No Datum of code " + codeStr); } continue; } else if (dbContext_) { auto authFactory = AuthorityFactory::create(NN_NO_CHECK(dbContext_), std::string()); auto list = authFactory->createObjectsFromName( datumName, {AuthorityFactory::ObjectType::DATUM}, false /* approximate=false*/); if (!list.empty()) { auto datum = util::nn_dynamic_pointer_cast(list.front()); if (!datum) throw ParsingException( "DatumEnsemble member is not a datum"); datums.push_back(NN_NO_CHECK(datum)); continue; } } // Fallback if no db match if (hasEllipsoid) { datums.emplace_back(GeodeticReferenceFrame::create( buildProperties(memberJ), buildEllipsoid(getObject(j, "ellipsoid")), optional(), PrimeMeridian::GREENWICH)); } else { datums.emplace_back( VerticalReferenceFrame::create(buildProperties(memberJ))); } } return DatumEnsemble::create( buildProperties(j), datums, PositionalAccuracy::create(getString(j, "accuracy"))); } // --------------------------------------------------------------------------- GeodeticReferenceFrameNNPtr JSONParser::buildGeodeticReferenceFrame(const json &j) { auto ellipsoidJ = getObject(j, "ellipsoid"); auto pm = j.contains("prime_meridian") ? buildPrimeMeridian(getObject(j, "prime_meridian")) : PrimeMeridian::GREENWICH; return GeodeticReferenceFrame::create( buildProperties(j), buildEllipsoid(ellipsoidJ), getAnchor(j), pm); } // --------------------------------------------------------------------------- DynamicGeodeticReferenceFrameNNPtr JSONParser::buildDynamicGeodeticReferenceFrame(const json &j) { auto ellipsoidJ = getObject(j, "ellipsoid"); auto pm = j.contains("prime_meridian") ? buildPrimeMeridian(getObject(j, "prime_meridian")) : PrimeMeridian::GREENWICH; Measure frameReferenceEpoch(getNumber(j, "frame_reference_epoch"), UnitOfMeasure::YEAR); optional deformationModel; if (j.contains("deformation_model")) { deformationModel = getString(j, "deformation_model"); } return DynamicGeodeticReferenceFrame::create( buildProperties(j), buildEllipsoid(ellipsoidJ), getAnchor(j), pm, frameReferenceEpoch, deformationModel); } // --------------------------------------------------------------------------- VerticalReferenceFrameNNPtr JSONParser::buildVerticalReferenceFrame(const json &j) { return VerticalReferenceFrame::create(buildProperties(j), getAnchor(j)); } // --------------------------------------------------------------------------- DynamicVerticalReferenceFrameNNPtr JSONParser::buildDynamicVerticalReferenceFrame(const json &j) { Measure frameReferenceEpoch(getNumber(j, "frame_reference_epoch"), UnitOfMeasure::YEAR); optional deformationModel; if (j.contains("deformation_model")) { deformationModel = getString(j, "deformation_model"); } return DynamicVerticalReferenceFrame::create( buildProperties(j), getAnchor(j), util::optional(), frameReferenceEpoch, deformationModel); } // --------------------------------------------------------------------------- PrimeMeridianNNPtr JSONParser::buildPrimeMeridian(const json &j) { if (!j.contains("longitude")) { throw ParsingException("Missing \"longitude\" key"); } auto longitude = j["longitude"]; if (longitude.is_number()) { return PrimeMeridian::create( buildProperties(j), Angle(longitude.get(), UnitOfMeasure::DEGREE)); } else if (longitude.is_object()) { return PrimeMeridian::create(buildProperties(j), Angle(getMeasure(longitude))); } throw ParsingException("Unexpected type for value of \"longitude\""); } // --------------------------------------------------------------------------- EllipsoidNNPtr JSONParser::buildEllipsoid(const json &j) { if (j.contains("semi_major_axis")) { auto semiMajorAxis = getLength(j, "semi_major_axis"); const auto celestialBody( Ellipsoid::guessBodyName(dbContext_, semiMajorAxis.getSIValue())); if (j.contains("semi_minor_axis")) { return Ellipsoid::createTwoAxis(buildProperties(j), semiMajorAxis, getLength(j, "semi_minor_axis"), celestialBody); } else if (j.contains("inverse_flattening")) { return Ellipsoid::createFlattenedSphere( buildProperties(j), semiMajorAxis, Scale(getNumber(j, "inverse_flattening")), celestialBody); } else { throw ParsingException( "Missing semi_minor_axis or inverse_flattening"); } } else if (j.contains("radius")) { auto radius = getLength(j, "radius"); const auto celestialBody( Ellipsoid::guessBodyName(dbContext_, radius.getSIValue())); return Ellipsoid::createSphere(buildProperties(j), radius, celestialBody); } throw ParsingException("Missing semi_major_axis or radius"); } // --------------------------------------------------------------------------- static BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules, PJ_CONTEXT *ctx) { if (!text.empty() && text[0] == '{') { json j; try { j = json::parse(text); } catch (const std::exception &e) { throw ParsingException(e.what()); } return JSONParser().attachDatabaseContext(dbContext).create(j); } if (!ci_starts_with(text, "step proj=") && !ci_starts_with(text, "step +proj=")) { for (const auto &wktConstant : WKTConstants::constants()) { if (ci_starts_with(text, wktConstant)) { for (auto wkt = text.c_str() + wktConstant.size(); *wkt != '\0'; ++wkt) { if (isspace(static_cast(*wkt))) continue; if (*wkt == '[') { return WKTParser() .attachDatabaseContext(dbContext) .setStrict(false) .createFromWKT(text); } break; } } } } const char *textWithoutPlusPrefix = text.c_str(); if (textWithoutPlusPrefix[0] == '+') textWithoutPlusPrefix++; if (strncmp(textWithoutPlusPrefix, "proj=", strlen("proj=")) == 0 || text.find(" +proj=") != std::string::npos || text.find(" proj=") != std::string::npos || strncmp(textWithoutPlusPrefix, "init=", strlen("init=")) == 0 || text.find(" +init=") != std::string::npos || text.find(" init=") != std::string::npos || strncmp(textWithoutPlusPrefix, "title=", strlen("title=")) == 0) { return PROJStringParser() .attachDatabaseContext(dbContext) .attachContext(ctx) .setUsePROJ4InitRules(ctx != nullptr ? (proj_context_get_use_proj4_init_rules( ctx, false) == TRUE) : usePROJ4InitRules) .createFromPROJString(text); } auto tokens = split(text, ':'); if (tokens.size() == 2) { if (!dbContext) { throw ParsingException("no database context specified"); } DatabaseContextNNPtr dbContextNNPtr(NN_NO_CHECK(dbContext)); const auto &authName = tokens[0]; const auto &code = tokens[1]; auto factory = AuthorityFactory::create(dbContextNNPtr, authName); try { return factory->createCoordinateReferenceSystem(code); } catch (...) { // Convenience for well-known misused code // See https://github.com/OSGeo/PROJ/issues/1730 if (ci_equal(authName, "EPSG") && code == "102100") { factory = AuthorityFactory::create(dbContextNNPtr, "ESRI"); return factory->createCoordinateReferenceSystem(code); } const auto authorities = dbContextNNPtr->getAuthorities(); for (const auto &authCandidate : authorities) { if (ci_equal(authCandidate, authName)) { factory = AuthorityFactory::create(dbContextNNPtr, authCandidate); try { return factory->createCoordinateReferenceSystem(code); } catch (...) { // EPSG:4326+3855 auto tokensCode = split(code, '+'); if (tokensCode.size() == 2) { auto crs1(factory->createCoordinateReferenceSystem( tokensCode[0], false)); auto crs2(factory->createCoordinateReferenceSystem( tokensCode[1], false)); return CompoundCRS::create( util::PropertyMap().set( IdentifiedObject::NAME_KEY, crs1->nameStr() + " + " + crs2->nameStr()), {crs1, crs2}); } throw; } } } throw; } } if (starts_with(text, "urn:ogc:def:crs,")) { if (!dbContext) { throw ParsingException("no database context specified"); } auto tokensComma = split(text, ','); if (tokensComma.size() == 4 && starts_with(tokensComma[1], "crs:") && starts_with(tokensComma[2], "cs:") && starts_with(tokensComma[3], "coordinateOperation:")) { // OGC 07-092r2: para 7.5.4 // URN combined references for projected or derived CRSs const auto &crsPart = tokensComma[1]; const auto tokensCRS = split(crsPart, ':'); if (tokensCRS.size() != 4) { throw ParsingException( concat("invalid crs component: ", crsPart)); } auto factoryCRS = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensCRS[1]); auto baseCRS = factoryCRS->createCoordinateReferenceSystem(tokensCRS[3], true); const auto &csPart = tokensComma[2]; auto tokensCS = split(csPart, ':'); if (tokensCS.size() != 4) { throw ParsingException( concat("invalid cs component: ", csPart)); } auto factoryCS = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensCS[1]); auto cs = factoryCS->createCoordinateSystem(tokensCS[3]); const auto &opPart = tokensComma[3]; auto tokensOp = split(opPart, ':'); if (tokensOp.size() != 4) { throw ParsingException( concat("invalid coordinateOperation component: ", opPart)); } auto factoryOp = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokensOp[1]); auto op = factoryOp->createCoordinateOperation(tokensOp[3], true); if (dynamic_cast(baseCRS.get()) && dynamic_cast(op.get()) && dynamic_cast(cs.get())) { auto geogCRS = NN_NO_CHECK( util::nn_dynamic_pointer_cast(baseCRS)); auto name = op->nameStr() + " / " + baseCRS->nameStr(); if (geogCRS->coordinateSystem()->axisList().size() == 3 && baseCRS->nameStr().find("3D") == std::string::npos) { name += " (3D)"; } return ProjectedCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, name), geogCRS, NN_NO_CHECK(util::nn_dynamic_pointer_cast(op)), NN_NO_CHECK( util::nn_dynamic_pointer_cast(cs))); } else if (dynamic_cast(baseCRS.get()) && !dynamic_cast(baseCRS.get()) && dynamic_cast(op.get()) && dynamic_cast(cs.get())) { return DerivedGeodeticCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, op->nameStr() + " / " + baseCRS->nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast(baseCRS)), NN_NO_CHECK(util::nn_dynamic_pointer_cast(op)), NN_NO_CHECK( util::nn_dynamic_pointer_cast(cs))); } else if (dynamic_cast(baseCRS.get()) && dynamic_cast(op.get()) && dynamic_cast(cs.get())) { return DerivedGeographicCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, op->nameStr() + " / " + baseCRS->nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast(baseCRS)), NN_NO_CHECK(util::nn_dynamic_pointer_cast(op)), NN_NO_CHECK( util::nn_dynamic_pointer_cast(cs))); } else if (dynamic_cast(baseCRS.get()) && dynamic_cast(op.get())) { return DerivedProjectedCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, op->nameStr() + " / " + baseCRS->nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast(baseCRS)), NN_NO_CHECK(util::nn_dynamic_pointer_cast(op)), cs); } else if (dynamic_cast(baseCRS.get()) && dynamic_cast(op.get()) && dynamic_cast(cs.get())) { return DerivedVerticalCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, op->nameStr() + " / " + baseCRS->nameStr()), NN_NO_CHECK( util::nn_dynamic_pointer_cast(baseCRS)), NN_NO_CHECK(util::nn_dynamic_pointer_cast(op)), NN_NO_CHECK(util::nn_dynamic_pointer_cast(cs))); } else { throw ParsingException("unsupported combination of baseCRS, CS " "and coordinateOperation for a " "DerivedCRS"); } } // OGC 07-092r2: para 7.5.2 // URN combined references for compound coordinate reference systems std::vector components; std::string name; for (size_t i = 1; i < tokensComma.size(); i++) { tokens = split(tokensComma[i], ':'); if (tokens.size() != 4) { throw ParsingException( concat("invalid crs component: ", tokensComma[i])); } const auto &type = tokens[0]; auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); const auto &code = tokens[3]; if (type == "crs") { auto crs(factory->createCoordinateReferenceSystem(code, false)); components.emplace_back(crs); if (!name.empty()) { name += " + "; } name += crs->nameStr(); } else { throw ParsingException( concat("unexpected object type: ", type)); } } return CompoundCRS::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, name), components); } // OGC 07-092r2: para 7.5.3 // 7.5.3 URN combined references for concatenated operations if (starts_with(text, "urn:ogc:def:coordinateOperation,")) { if (!dbContext) { throw ParsingException("no database context specified"); } auto tokensComma = split(text, ','); std::vector components; for (size_t i = 1; i < tokensComma.size(); i++) { tokens = split(tokensComma[i], ':'); if (tokens.size() != 4) { throw ParsingException(concat( "invalid coordinateOperation component: ", tokensComma[i])); } const auto &type = tokens[0]; auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[1]); const auto &code = tokens[3]; if (type == "coordinateOperation") { auto op(factory->createCoordinateOperation(code, false)); components.emplace_back(op); } else { throw ParsingException( concat("unexpected object type: ", type)); } } return ConcatenatedOperation::createComputeMetadata(components, true); } // urn:ogc:def:crs:EPSG::4326 if (tokens.size() == 7) { if (!dbContext) { throw ParsingException("no database context specified"); } const auto &type = tokens[3]; auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), tokens[4]); const auto &code = tokens[6]; if (type == "crs") { return factory->createCoordinateReferenceSystem(code); } if (type == "coordinateOperation") { return factory->createCoordinateOperation(code, true); } if (type == "datum") { return factory->createDatum(code); } if (type == "ellipsoid") { return factory->createEllipsoid(code); } if (type == "meridian") { return factory->createPrimeMeridian(code); } throw ParsingException(concat("unhandled object type: ", type)); } if (dbContext) { auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string()); // First pass: exact match on CRS objects // Second pass: exact match on other objects // Third pass: approximate match on CRS objects // Fourth pass: approximate match on other objects constexpr size_t limitResultCount = 10; for (int pass = 0; pass <= 3; ++pass) { const bool approximateMatch = (pass >= 2); auto res = factory->createObjectsFromName( text, (pass == 0 || pass == 2) ? std::vector< AuthorityFactory::ObjectType>{AuthorityFactory:: ObjectType::CRS} : std::vector< AuthorityFactory:: ObjectType>{AuthorityFactory::ObjectType:: ELLIPSOID, AuthorityFactory::ObjectType::DATUM, AuthorityFactory::ObjectType:: COORDINATE_OPERATION}, approximateMatch, limitResultCount); if (res.size() == 1) { return res.front(); } if (res.size() > 1) { if (pass == 0 || pass == 2) { for (size_t ndim = 2; ndim <= 3; ndim++) { for (const auto &obj : res) { auto crs = dynamic_cast(obj.get()); if (crs && crs->coordinateSystem()->axisList().size() == ndim) { return obj; } } } } std::string msg("several objects matching this name: "); bool first = true; for (const auto &obj : res) { if (msg.size() > 200) { msg += ", ..."; break; } if (!first) { msg += ", "; } first = false; msg += obj->nameStr(); } throw ParsingException(msg); } } } throw ParsingException("unrecognized format / unknown name"); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a sub-class of BaseObject from a user specified text. * * The text can be a: *
    *
  • WKT string
  • *
  • PROJ string
  • *
  • database code, prefixed by its authoriy. e.g. "EPSG:4326"
  • *
  • OGC URN. e.g. "urn:ogc:def:crs:EPSG::4326", * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"
  • *
  • OGC URN combining references for compound coordinate reference systems * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" * We also accept a custom abbreviated syntax EPSG:2393+5717 *
  • *
  • OGC URN combining references for references for projected or derived * CRSs * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)" * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031" *
  • *
  • OGC URN combining references for concatenated operations * e.g. * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"
  • *
  • an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.
  • *
  • PROJJSON string
  • *
* * @param text One of the above mentioned text format * @param dbContext Database context, or nullptr (in which case database * lookups will not work) * @param usePROJ4InitRules When set to true, * init=epsg:XXXX syntax will be allowed and will be interpreted according to * PROJ.4 and PROJ.5 rules, that is geodeticCRS will have longitude, latitude * order and will expect/output coordinates in radians. ProjectedCRS will have * easting, northing axis order (except the ones with Transverse Mercator South * Orientated projection). In that mode, the epsg:XXXX syntax will be also * interprated the same way. * @throw ParsingException */ BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules) { return createFromUserInput(text, dbContext, usePROJ4InitRules, nullptr); } // --------------------------------------------------------------------------- /** \brief Instantiate a sub-class of BaseObject from a user specified text. * * The text can be a: *
    *
  • WKT string
  • *
  • PROJ string
  • *
  • database code, prefixed by its authoriy. e.g. "EPSG:4326"
  • *
  • OGC URN. e.g. "urn:ogc:def:crs:EPSG::4326", * "urn:ogc:def:coordinateOperation:EPSG::1671", * "urn:ogc:def:ellipsoid:EPSG::7001" * or "urn:ogc:def:datum:EPSG::6326"
  • *
  • OGC URN combining references for compound coordinate reference systems * e.g. "urn:ogc:def:crs,crs:EPSG::2393,crs:EPSG::5717" * We also accept a custom abbreviated syntax EPSG:2393+5717 *
  • *
  • OGC URN combining references for references for projected or derived * CRSs * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)" * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031" *
  • *
  • OGC URN combining references for concatenated operations * e.g. * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"
  • *
  • an Object name. e.g "WGS 84", "WGS 84 / UTM zone 31N". In that case as * uniqueness is not guaranteed, the function may apply heuristics to * determine the appropriate best match.
  • *
  • PROJJSON string
  • *
* * @param text One of the above mentioned text format * @param ctx PROJ context * @throw ParsingException */ BaseObjectNNPtr createFromUserInput(const std::string &text, PJ_CONTEXT *ctx) { DatabaseContextPtr dbContext; try { if (ctx != nullptr && ctx->cpp_context) { dbContext = ctx->cpp_context->getDatabaseContext().as_nullable(); } } catch (const std::exception &) { } return createFromUserInput(text, dbContext, false, ctx); } // --------------------------------------------------------------------------- /** \brief Instantiate a sub-class of BaseObject from a WKT string. * * By default, validation is strict (to the extent of the checks that are * actually implemented. Currently only WKT1 strict grammar is checked), and * any issue detected will cause an exception to be thrown, unless * setStrict(false) is called priorly. * * In non-strict mode, non-fatal issues will be recovered and simply listed * in warningList(). This does not prevent more severe errors to cause an * exception to be thrown. * * @throw ParsingException */ BaseObjectNNPtr WKTParser::createFromWKT(const std::string &wkt) { WKTNodeNNPtr root = WKTNode::createFrom(wkt); auto obj = d->build(root); const auto dialect = guessDialect(wkt); if (dialect == WKTGuessedDialect::WKT1_GDAL || dialect == WKTGuessedDialect::WKT1_ESRI) { auto errorMsg = pj_wkt1_parse(wkt); if (!errorMsg.empty()) { d->emitRecoverableWarning(errorMsg); } } else if (dialect == WKTGuessedDialect::WKT2_2015 || dialect == WKTGuessedDialect::WKT2_2019) { auto errorMsg = pj_wkt2_parse(wkt); if (!errorMsg.empty()) { d->emitRecoverableWarning(errorMsg); } } return obj; } // --------------------------------------------------------------------------- /** \brief Attach a database context, to allow queries in it if needed. */ WKTParser & WKTParser::attachDatabaseContext(const DatabaseContextPtr &dbContext) { d->dbContext_ = dbContext; return *this; } // --------------------------------------------------------------------------- /** \brief Guess the "dialect" of the WKT string. */ WKTParser::WKTGuessedDialect WKTParser::guessDialect(const std::string &wkt) noexcept { const std::string *const wkt1_keywords[] = { &WKTConstants::GEOCCS, &WKTConstants::GEOGCS, &WKTConstants::COMPD_CS, &WKTConstants::PROJCS, &WKTConstants::VERT_CS, &WKTConstants::LOCAL_CS}; for (const auto &pointerKeyword : wkt1_keywords) { if (ci_starts_with(wkt, *pointerKeyword)) { if (ci_find(wkt, "GEOGCS[\"GCS_") != std::string::npos) { return WKTGuessedDialect::WKT1_ESRI; } return WKTGuessedDialect::WKT1_GDAL; } } const std::string *const wkt2_2019_only_keywords[] = { &WKTConstants::GEOGCRS, // contained in previous one // &WKTConstants::BASEGEOGCRS, &WKTConstants::CONCATENATEDOPERATION, &WKTConstants::USAGE, &WKTConstants::DYNAMIC, &WKTConstants::FRAMEEPOCH, &WKTConstants::MODEL, &WKTConstants::VELOCITYGRID, &WKTConstants::ENSEMBLE, &WKTConstants::DERIVEDPROJCRS, &WKTConstants::BASEPROJCRS, &WKTConstants::GEOGRAPHICCRS, &WKTConstants::TRF, &WKTConstants::VRF}; for (const auto &pointerKeyword : wkt2_2019_only_keywords) { auto pos = ci_find(wkt, *pointerKeyword); if (pos != std::string::npos && wkt[pos + pointerKeyword->size()] == '[') { return WKTGuessedDialect::WKT2_2019; } } static const char *const wkt2_2019_only_substrings[] = { "CS[TemporalDateTime,", "CS[TemporalCount,", "CS[TemporalMeasure,", }; for (const auto &substrings : wkt2_2019_only_substrings) { if (ci_find(wkt, substrings) != std::string::npos) { return WKTGuessedDialect::WKT2_2019; } } for (const auto &wktConstant : WKTConstants::constants()) { if (ci_starts_with(wkt, wktConstant)) { for (auto wktPtr = wkt.c_str() + wktConstant.size(); *wktPtr != '\0'; ++wktPtr) { if (isspace(static_cast(*wktPtr))) continue; if (*wktPtr == '[') { return WKTGuessedDialect::WKT2_2015; } break; } } } return WKTGuessedDialect::NOT_WKT; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress FormattingException::FormattingException(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- FormattingException::FormattingException(const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- FormattingException::FormattingException(const FormattingException &) = default; // --------------------------------------------------------------------------- FormattingException::~FormattingException() = default; // --------------------------------------------------------------------------- void FormattingException::Throw(const char *msg) { throw FormattingException(msg); } // --------------------------------------------------------------------------- void FormattingException::Throw(const std::string &msg) { throw FormattingException(msg); } // --------------------------------------------------------------------------- ParsingException::ParsingException(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- ParsingException::ParsingException(const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- ParsingException::ParsingException(const ParsingException &) = default; // --------------------------------------------------------------------------- ParsingException::~ParsingException() = default; // --------------------------------------------------------------------------- IPROJStringExportable::~IPROJStringExportable() = default; // --------------------------------------------------------------------------- std::string IPROJStringExportable::exportToPROJString( PROJStringFormatter *formatter) const { const bool bIsCRS = dynamic_cast(this) != nullptr; if (bIsCRS) { formatter->setCRSExport(true); } _exportToPROJString(formatter); if (formatter->getAddNoDefs() && bIsCRS) { if (!formatter->hasParam("no_defs")) { formatter->addParam("no_defs"); } } if (bIsCRS) { if (!formatter->hasParam("type")) { formatter->addParam("type", "crs"); } formatter->setCRSExport(false); } return formatter->toString(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Step { std::string name{}; bool isInit = false; bool inverted{false}; struct KeyValue { std::string key{}; std::string value{}; bool usedByParser = false; // only for PROJStringParser used explicit KeyValue(const std::string &keyIn) : key(keyIn) {} KeyValue(const char *keyIn, const std::string &valueIn); KeyValue(const std::string &keyIn, const std::string &valueIn) : key(keyIn), value(valueIn) {} // cppcheck-suppress functionStatic bool keyEquals(const char *otherKey) const noexcept { return key == otherKey; } // cppcheck-suppress functionStatic bool equals(const char *otherKey, const char *otherVal) const noexcept { return key == otherKey && value == otherVal; } bool operator==(const KeyValue &other) const noexcept { return key == other.key && value == other.value; } bool operator!=(const KeyValue &other) const noexcept { return key != other.key || value != other.value; } }; std::vector paramValues{}; bool hasKey(const char *keyName) const { for (const auto &kv : paramValues) { if (kv.key == keyName) { return true; } } return false; } }; Step::KeyValue::KeyValue(const char *keyIn, const std::string &valueIn) : key(keyIn), value(valueIn) {} struct PROJStringFormatter::Private { PROJStringFormatter::Convention convention_ = PROJStringFormatter::Convention::PROJ_5; std::vector toWGS84Parameters_{}; std::string vDatumExtension_{}; std::string hDatumExtension_{}; std::list steps_{}; std::vector globalParamValues_{}; struct InversionStackElt { std::list::iterator startIter{}; bool iterValid = false; bool currentInversionState = false; }; std::vector inversionStack_{InversionStackElt()}; bool omitProjLongLatIfPossible_ = false; std::vector omitZUnitConversion_{false}; DatabaseContextPtr dbContext_{}; bool useApproxTMerc_ = false; bool addNoDefs_ = true; bool coordOperationOptimizations_ = false; bool crsExport_ = false; bool legacyCRSToCRSContext_ = false; std::string result_{}; // cppcheck-suppress functionStatic void appendToResult(const char *str); // cppcheck-suppress functionStatic void addStep(); }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PROJStringFormatter::PROJStringFormatter(Convention conventionIn, const DatabaseContextPtr &dbContext) : d(internal::make_unique()) { d->convention_ = conventionIn; d->dbContext_ = dbContext; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PROJStringFormatter::~PROJStringFormatter() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Constructs a new formatter. * * A formatter can be used only once (its internal state is mutated) * * Its default behaviour can be adjusted with the different setters. * * @param conventionIn PROJ string flavor. Defaults to Convention::PROJ_5 * @param dbContext Database context (can help to find alternative grid names). * May be nullptr * @return new formatter. */ PROJStringFormatterNNPtr PROJStringFormatter::create(Convention conventionIn, DatabaseContextPtr dbContext) { return NN_NO_CHECK(PROJStringFormatter::make_unique( conventionIn, dbContext)); } // --------------------------------------------------------------------------- /** \brief Set whether approximate Transverse Mercator or UTM should be used */ void PROJStringFormatter::setUseApproxTMerc(bool flag) { d->useApproxTMerc_ = flag; } // --------------------------------------------------------------------------- /** \brief Returns the PROJ string. */ const std::string &PROJStringFormatter::toString() const { assert(d->inversionStack_.size() == 1); d->result_.clear(); for (auto iter = d->steps_.begin(); iter != d->steps_.end();) { // Remove no-op helmert auto &step = *iter; const auto paramCount = step.paramValues.size(); if (step.name == "helmert" && (paramCount == 3 || paramCount == 8) && step.paramValues[0].equals("x", "0") && step.paramValues[1].equals("y", "0") && step.paramValues[2].equals("z", "0") && (paramCount == 3 || (step.paramValues[3].equals("rx", "0") && step.paramValues[4].equals("ry", "0") && step.paramValues[5].equals("rz", "0") && step.paramValues[6].equals("s", "0") && step.paramValues[7].keyEquals("convention")))) { iter = d->steps_.erase(iter); } else if (d->coordOperationOptimizations_ && step.name == "unitconvert" && paramCount == 2 && step.paramValues[0].keyEquals("xy_in") && step.paramValues[1].keyEquals("xy_out") && step.paramValues[0].value == step.paramValues[1].value) { iter = d->steps_.erase(iter); } else if (step.name == "push" && step.inverted) { step.name = "pop"; step.inverted = false; } else if (step.name == "pop" && step.inverted) { step.name = "push"; step.inverted = false; } else { ++iter; } } for (auto &step : d->steps_) { if (!step.inverted) { continue; } const auto paramCount = step.paramValues.size(); // axisswap order=2,1 is its own inverse if (step.name == "axisswap" && paramCount == 1 && step.paramValues[0].equals("order", "2,1")) { step.inverted = false; continue; } // handle unitconvert inverse if (step.name == "unitconvert" && paramCount == 2 && step.paramValues[0].keyEquals("xy_in") && step.paramValues[1].keyEquals("xy_out")) { std::swap(step.paramValues[0].value, step.paramValues[1].value); step.inverted = false; continue; } if (step.name == "unitconvert" && paramCount == 2 && step.paramValues[0].keyEquals("z_in") && step.paramValues[1].keyEquals("z_out")) { std::swap(step.paramValues[0].value, step.paramValues[1].value); step.inverted = false; continue; } if (step.name == "unitconvert" && paramCount == 4 && step.paramValues[0].keyEquals("xy_in") && step.paramValues[1].keyEquals("z_in") && step.paramValues[2].keyEquals("xy_out") && step.paramValues[3].keyEquals("z_out")) { std::swap(step.paramValues[0].value, step.paramValues[2].value); std::swap(step.paramValues[1].value, step.paramValues[3].value); step.inverted = false; continue; } } bool changeDone; do { changeDone = false; auto iterPrev = d->steps_.begin(); if (iterPrev == d->steps_.end()) { break; } auto iterCur = iterPrev; iterCur++; for (size_t i = 1; i < d->steps_.size(); ++i, ++iterCur, ++iterPrev) { auto &prevStep = *iterPrev; auto &curStep = *iterCur; const auto curStepParamCount = curStep.paramValues.size(); const auto prevStepParamCount = prevStep.paramValues.size(); // longlat (or its inverse) with ellipsoid only is a no-op // do that only for an internal step if (i + 1 < d->steps_.size() && curStep.name == "longlat" && curStepParamCount == 1 && curStep.paramValues[0].keyEquals("ellps")) { d->steps_.erase(iterCur); changeDone = true; break; } // push v_x followed by pop v_x is a no-op. if (curStep.name == "pop" && prevStep.name == "push" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 1 && prevStepParamCount == 1 && curStep.paramValues[0].key == prevStep.paramValues[0].key) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // pop v_x followed by push v_x is, almost, a no-op. For our // purposes, // we consider it as a no-op for better pipeline optimizations. if (curStep.name == "push" && prevStep.name == "pop" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 1 && prevStepParamCount == 1 && curStep.paramValues[0].key == prevStep.paramValues[0].key) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // unitconvert (xy) followed by its inverse is a no-op if (curStep.name == "unitconvert" && prevStep.name == "unitconvert" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 2 && prevStepParamCount == 2 && curStep.paramValues[0].keyEquals("xy_in") && prevStep.paramValues[0].keyEquals("xy_in") && curStep.paramValues[1].keyEquals("xy_out") && prevStep.paramValues[1].keyEquals("xy_out") && curStep.paramValues[0].value == prevStep.paramValues[1].value && curStep.paramValues[1].value == prevStep.paramValues[0].value) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // unitconvert (z) followed by its inverse is a no-op if (curStep.name == "unitconvert" && prevStep.name == "unitconvert" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 2 && prevStepParamCount == 2 && curStep.paramValues[0].keyEquals("z_in") && prevStep.paramValues[0].keyEquals("z_in") && curStep.paramValues[1].keyEquals("z_out") && prevStep.paramValues[1].keyEquals("z_out") && curStep.paramValues[0].value == prevStep.paramValues[1].value && curStep.paramValues[1].value == prevStep.paramValues[0].value) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // unitconvert (xyz) followed by its inverse is a no-op if (curStep.name == "unitconvert" && prevStep.name == "unitconvert" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 4 && prevStepParamCount == 4 && curStep.paramValues[0].keyEquals("xy_in") && prevStep.paramValues[0].keyEquals("xy_in") && curStep.paramValues[1].keyEquals("z_in") && prevStep.paramValues[1].keyEquals("z_in") && curStep.paramValues[2].keyEquals("xy_out") && prevStep.paramValues[2].keyEquals("xy_out") && curStep.paramValues[3].keyEquals("z_out") && prevStep.paramValues[3].keyEquals("z_out") && curStep.paramValues[0].value == prevStep.paramValues[2].value && curStep.paramValues[1].value == prevStep.paramValues[3].value && curStep.paramValues[2].value == prevStep.paramValues[0].value && curStep.paramValues[3].value == prevStep.paramValues[1].value) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // combine unitconvert (xy) and unitconvert (z) for (int k = 0; k < 2; ++k) { auto &first = (k == 0) ? curStep : prevStep; auto &second = (k == 0) ? prevStep : curStep; if (first.name == "unitconvert" && second.name == "unitconvert" && !first.inverted && !second.inverted && first.paramValues.size() == 2 && second.paramValues.size() == 2 && second.paramValues[0].keyEquals("xy_in") && second.paramValues[1].keyEquals("xy_out") && first.paramValues[0].keyEquals("z_in") && first.paramValues[1].keyEquals("z_out")) { auto xy_in = second.paramValues[0].value; auto xy_out = second.paramValues[1].value; auto z_in = first.paramValues[0].value; auto z_out = first.paramValues[1].value; d->steps_.erase(iterPrev, iterCur); iterCur->paramValues.clear(); iterCur->paramValues.emplace_back( Step::KeyValue("xy_in", xy_in)); iterCur->paramValues.emplace_back( Step::KeyValue("z_in", z_in)); iterCur->paramValues.emplace_back( Step::KeyValue("xy_out", xy_out)); iterCur->paramValues.emplace_back( Step::KeyValue("z_out", z_out)); changeDone = true; break; } } if (changeDone) { break; } // +step +proj=unitconvert +xy_in=X1 +xy_out=X2 // +step +proj=unitconvert +xy_in=X2 +z_in=Z1 +xy_out=X1 +z_out=Z2 // ==> step +proj=unitconvert +z_in=Z1 +z_out=Z2 for (int k = 0; k < 2; ++k) { auto &first = (k == 0) ? curStep : prevStep; auto &second = (k == 0) ? prevStep : curStep; if (first.name == "unitconvert" && second.name == "unitconvert" && !first.inverted && !second.inverted && first.paramValues.size() == 4 && second.paramValues.size() == 2 && first.paramValues[0].keyEquals("xy_in") && first.paramValues[1].keyEquals("z_in") && first.paramValues[2].keyEquals("xy_out") && first.paramValues[3].keyEquals("z_out") && second.paramValues[0].keyEquals("xy_in") && second.paramValues[1].keyEquals("xy_out") && first.paramValues[0].value == second.paramValues[1].value && first.paramValues[2].value == second.paramValues[0].value) { auto z_in = first.paramValues[1].value; auto z_out = first.paramValues[3].value; if (z_in != z_out) { d->steps_.erase(iterPrev, iterCur); iterCur->paramValues.clear(); iterCur->paramValues.emplace_back( Step::KeyValue("z_in", z_in)); iterCur->paramValues.emplace_back( Step::KeyValue("z_out", z_out)); } else { ++iterCur; d->steps_.erase(iterPrev, iterCur); } changeDone = true; break; } } if (changeDone) { break; } // unitconvert (1), axisswap order=2,1, unitconvert(2) ==> // axisswap order=2,1, unitconvert (1), unitconvert(2) which // will get further optimized by previous case if (i + 1 < d->steps_.size() && prevStep.name == "unitconvert" && curStep.name == "axisswap" && curStepParamCount == 1 && curStep.paramValues[0].equals("order", "2,1")) { auto iterNext = iterCur; ++iterNext; auto &nextStep = *iterNext; if (nextStep.name == "unitconvert") { std::swap(*iterPrev, *iterCur); changeDone = true; break; } } // axisswap order=2,1 followed by itself is a no-op if (curStep.name == "axisswap" && prevStep.name == "axisswap" && curStepParamCount == 1 && prevStepParamCount == 1 && curStep.paramValues[0].equals("order", "2,1") && prevStep.paramValues[0].equals("order", "2,1")) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } // axisswap order=2,1, unitconvert, axisswap order=2,1 -> can // suppress axisswap if (i + 1 < d->steps_.size() && prevStep.name == "axisswap" && curStep.name == "unitconvert" && prevStepParamCount == 1 && prevStep.paramValues[0].equals("order", "2,1")) { auto iterNext = iterCur; ++iterNext; auto &nextStep = *iterNext; if (nextStep.name == "axisswap" && nextStep.paramValues.size() == 1 && nextStep.paramValues[0].equals("order", "2,1")) { d->steps_.erase(iterPrev); d->steps_.erase(iterNext); changeDone = true; break; } } // for practical purposes WGS84 and GRS80 ellipsoids are // equivalents (cartesian transform between both lead to differences // of the order of 1e-14 deg..). // No need to do a cart roundtrip for that... // and actually IGNF uses the GRS80 definition for the WGS84 datum if (curStep.name == "cart" && prevStep.name == "cart" && curStep.inverted == !prevStep.inverted && curStepParamCount == 1 && prevStepParamCount == 1 && ((curStep.paramValues[0].equals("ellps", "WGS84") && prevStep.paramValues[0].equals("ellps", "GRS80")) || (curStep.paramValues[0].equals("ellps", "GRS80") && prevStep.paramValues[0].equals("ellps", "WGS84")))) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } if (curStep.name == "helmert" && prevStep.name == "helmert" && !curStep.inverted && !prevStep.inverted && curStepParamCount == 3 && curStepParamCount == prevStepParamCount) { std::map leftParamsMap; std::map rightParamsMap; try { for (const auto &kv : prevStep.paramValues) { leftParamsMap[kv.key] = c_locale_stod(kv.value); } for (const auto &kv : curStep.paramValues) { rightParamsMap[kv.key] = c_locale_stod(kv.value); } } catch (const std::invalid_argument &) { break; } const std::string x("x"); const std::string y("y"); const std::string z("z"); if (leftParamsMap.find(x) != leftParamsMap.end() && leftParamsMap.find(y) != leftParamsMap.end() && leftParamsMap.find(z) != leftParamsMap.end() && rightParamsMap.find(x) != rightParamsMap.end() && rightParamsMap.find(y) != rightParamsMap.end() && rightParamsMap.find(z) != rightParamsMap.end()) { const double xSum = leftParamsMap[x] + rightParamsMap[x]; const double ySum = leftParamsMap[y] + rightParamsMap[y]; const double zSum = leftParamsMap[z] + rightParamsMap[z]; if (xSum == 0.0 && ySum == 0.0 && zSum == 0.0) { ++iterCur; d->steps_.erase(iterPrev, iterCur); } else { prevStep.paramValues[0] = Step::KeyValue("x", internal::toString(xSum)); prevStep.paramValues[1] = Step::KeyValue("y", internal::toString(ySum)); prevStep.paramValues[2] = Step::KeyValue("z", internal::toString(zSum)); d->steps_.erase(iterCur); } changeDone = true; break; } } // hermert followed by its inverse is a no-op if (curStep.name == "helmert" && prevStep.name == "helmert" && !curStep.inverted && !prevStep.inverted && curStepParamCount == prevStepParamCount) { std::set leftParamsSet; std::set rightParamsSet; std::map leftParamsMap; std::map rightParamsMap; for (const auto &kv : prevStep.paramValues) { leftParamsSet.insert(kv.key); leftParamsMap[kv.key] = kv.value; } for (const auto &kv : curStep.paramValues) { rightParamsSet.insert(kv.key); rightParamsMap[kv.key] = kv.value; } if (leftParamsSet == rightParamsSet) { bool doErase = true; try { for (const auto ¶m : leftParamsSet) { if (param == "convention" || param == "t_epoch" || param == "t_obs") { if (leftParamsMap[param] != rightParamsMap[param]) { doErase = false; break; } } else if (c_locale_stod(leftParamsMap[param]) != -c_locale_stod(rightParamsMap[param])) { doErase = false; break; } } } catch (const std::invalid_argument &) { break; } if (doErase) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } } } // +step +proj=hgridshift +grids=grid_A // +step +proj=vgridshift [...] <== curStep // +step +inv +proj=hgridshift +grids=grid_A // ==> // +step +proj=push +v_1 +v_2 // +step +proj=hgridshift +grids=grid_A +omit_inv // +step +proj=vgridshift [...] // +step +inv +proj=hgridshift +grids=grid_A +omit_fwd // +step +proj=pop +v_1 +v_2 if (i + 1 < d->steps_.size() && prevStep.name == "hgridshift" && prevStepParamCount == 1 && curStep.name == "vgridshift") { auto iterNext = iterCur; ++iterNext; auto &nextStep = *iterNext; if (nextStep.name == "hgridshift" && nextStep.inverted != prevStep.inverted && nextStep.paramValues.size() == 1 && prevStep.paramValues[0] == nextStep.paramValues[0]) { Step pushStep; pushStep.name = "push"; pushStep.paramValues.emplace_back("v_1"); pushStep.paramValues.emplace_back("v_2"); d->steps_.insert(iterPrev, pushStep); prevStep.paramValues.emplace_back("omit_inv"); nextStep.paramValues.emplace_back("omit_fwd"); Step popStep; popStep.name = "pop"; popStep.paramValues.emplace_back("v_1"); popStep.paramValues.emplace_back("v_2"); ++iterNext; d->steps_.insert(iterNext, popStep); changeDone = true; break; } } // detect a step and its inverse if (curStep.inverted != prevStep.inverted && curStep.name == prevStep.name && curStepParamCount == prevStepParamCount) { bool allSame = true; for (size_t j = 0; j < curStepParamCount; j++) { if (curStep.paramValues[j] != prevStep.paramValues[j]) { allSame = false; break; } } if (allSame) { ++iterCur; d->steps_.erase(iterPrev, iterCur); changeDone = true; break; } } } } while (changeDone); if (d->steps_.size() > 1 || (d->steps_.size() == 1 && (d->steps_.front().inverted || d->steps_.front().hasKey("omit_inv") || d->steps_.front().hasKey("omit_fwd") || !d->globalParamValues_.empty()))) { d->appendToResult("+proj=pipeline"); for (const auto ¶mValue : d->globalParamValues_) { d->appendToResult("+"); d->result_ += paramValue.key; if (!paramValue.value.empty()) { d->result_ += '='; d->result_ += pj_double_quote_string_param_if_needed(paramValue.value); } } } for (const auto &step : d->steps_) { if (!d->result_.empty()) { d->appendToResult("+step"); } if (step.inverted) { d->appendToResult("+inv"); } if (!step.name.empty()) { d->appendToResult(step.isInit ? "+init=" : "+proj="); d->result_ += step.name; } for (const auto ¶mValue : step.paramValues) { d->appendToResult("+"); d->result_ += paramValue.key; if (!paramValue.value.empty()) { d->result_ += '='; d->result_ += pj_double_quote_string_param_if_needed(paramValue.value); } } } if (d->result_.empty()) { d->appendToResult("+proj=noop"); } return d->result_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PROJStringFormatter::Convention PROJStringFormatter::convention() const { return d->convention_; } // --------------------------------------------------------------------------- bool PROJStringFormatter::getUseApproxTMerc() const { return d->useApproxTMerc_; } // --------------------------------------------------------------------------- void PROJStringFormatter::setCoordinateOperationOptimizations(bool enable) { d->coordOperationOptimizations_ = enable; } // --------------------------------------------------------------------------- void PROJStringFormatter::Private::appendToResult(const char *str) { if (!result_.empty()) { result_ += ' '; } result_ += str; } // --------------------------------------------------------------------------- static void PROJStringSyntaxParser(const std::string &projString, std::vector &steps, std::vector &globalParamValues, std::string &title) { const char *c_str = projString.c_str(); std::vector tokens; bool hasProj = false; bool hasInit = false; bool hasPipeline = false; { size_t i = 0; while (true) { for (; isspace(static_cast(c_str[i])); i++) { } std::string token; bool in_string = false; for (; c_str[i]; i++) { if (in_string) { if (c_str[i] == '"' && c_str[i + 1] == '"') { i++; } else if (c_str[i] == '"') { in_string = false; continue; } } else if (c_str[i] == '=' && c_str[i + 1] == '"') { in_string = true; token += c_str[i]; i++; continue; } else if (isspace(static_cast(c_str[i]))) { break; } token += c_str[i]; } if (in_string) { throw ParsingException("Unbalanced double quote"); } if (token.empty()) { break; } if (!hasPipeline && (token == "proj=pipeline" || token == "+proj=pipeline")) { hasPipeline = true; } else if (!hasProj && (starts_with(token, "proj=") || starts_with(token, "+proj="))) { hasProj = true; } else if (!hasInit && (starts_with(token, "init=") || starts_with(token, "+init="))) { hasInit = true; } tokens.emplace_back(token); } } bool prevWasTitle = false; if (!hasPipeline) { if (hasProj || hasInit) { steps.push_back(Step()); } for (auto &word : tokens) { if (word[0] == '+') { word = word.substr(1); } else if (prevWasTitle && word.find('=') == std::string::npos) { title += " "; title += word; continue; } prevWasTitle = false; if (starts_with(word, "proj=") && !hasInit) { assert(hasProj); auto stepName = word.substr(strlen("proj=")); steps.back().name = stepName; } else if (starts_with(word, "init=")) { assert(hasInit); auto initName = word.substr(strlen("init=")); steps.back().name = initName; steps.back().isInit = true; } else if (word == "inv") { if (!steps.empty()) { steps.back().inverted = true; } } else if (starts_with(word, "title=")) { title = word.substr(strlen("title=")); prevWasTitle = true; } else if (word != "step") { const auto pos = word.find('='); auto key = word.substr(0, pos); auto pair = (pos != std::string::npos) ? Step::KeyValue(key, word.substr(pos + 1)) : Step::KeyValue(key); if (steps.empty()) { globalParamValues.push_back(pair); } else { steps.back().paramValues.push_back(pair); } } } return; } bool inPipeline = false; bool invGlobal = false; for (auto &word : tokens) { if (word[0] == '+') { word = word.substr(1); } else if (prevWasTitle && word.find('=') == std::string::npos) { title += " "; title += word; continue; } prevWasTitle = false; if (word == "proj=pipeline") { if (inPipeline) { throw ParsingException("nested pipeline not supported"); } inPipeline = true; } else if (word == "step") { if (!inPipeline) { throw ParsingException("+step found outside pipeline"); } steps.push_back(Step()); } else if (word == "inv") { if (steps.empty()) { invGlobal = true; } else { steps.back().inverted = true; } } else if (inPipeline && !steps.empty() && starts_with(word, "proj=") && steps.back().name.empty()) { auto stepName = word.substr(strlen("proj=")); steps.back().name = stepName; } else if (inPipeline && !steps.empty() && starts_with(word, "init=") && steps.back().name.empty()) { auto initName = word.substr(strlen("init=")); steps.back().name = initName; steps.back().isInit = true; } else if (!inPipeline && starts_with(word, "title=")) { title = word.substr(strlen("title=")); prevWasTitle = true; } else { const auto pos = word.find('='); auto key = word.substr(0, pos); auto pair = (pos != std::string::npos) ? Step::KeyValue(key, word.substr(pos + 1)) : Step::KeyValue(key); if (steps.empty()) { globalParamValues.push_back(pair); } else { steps.back().paramValues.push_back(pair); } } } if (invGlobal) { for (auto &step : steps) { step.inverted = !step.inverted; } std::reverse(steps.begin(), steps.end()); } } // --------------------------------------------------------------------------- void PROJStringFormatter::ingestPROJString( const std::string &str) // throw ParsingException { std::vector steps; std::string title; PROJStringSyntaxParser(str, steps, d->globalParamValues_, title); d->steps_.insert(d->steps_.end(), steps.begin(), steps.end()); } // --------------------------------------------------------------------------- void PROJStringFormatter::setCRSExport(bool b) { d->crsExport_ = b; } // --------------------------------------------------------------------------- bool PROJStringFormatter::getCRSExport() const { return d->crsExport_; } // --------------------------------------------------------------------------- void PROJStringFormatter::startInversion() { PROJStringFormatter::Private::InversionStackElt elt; elt.startIter = d->steps_.end(); if (elt.startIter != d->steps_.begin()) { elt.iterValid = true; --elt.startIter; // point to the last valid element } else { elt.iterValid = false; } elt.currentInversionState = !d->inversionStack_.back().currentInversionState; d->inversionStack_.push_back(elt); } // --------------------------------------------------------------------------- void PROJStringFormatter::stopInversion() { assert(!d->inversionStack_.empty()); auto startIter = d->inversionStack_.back().startIter; if (!d->inversionStack_.back().iterValid) { startIter = d->steps_.begin(); } else { ++startIter; // advance after the last valid element we marked above } // Invert the inversion status of the steps between the start point and // the current end of steps for (auto iter = startIter; iter != d->steps_.end(); ++iter) { iter->inverted = !iter->inverted; for (auto ¶mValue : iter->paramValues) { if (paramValue.key == "omit_fwd") paramValue.key = "omit_inv"; else if (paramValue.key == "omit_inv") paramValue.key = "omit_fwd"; } } // And reverse the order of steps in that range as well. std::reverse(startIter, d->steps_.end()); d->inversionStack_.pop_back(); } // --------------------------------------------------------------------------- bool PROJStringFormatter::isInverted() const { return d->inversionStack_.back().currentInversionState; } // --------------------------------------------------------------------------- void PROJStringFormatter::Private::addStep() { steps_.emplace_back(Step()); } // --------------------------------------------------------------------------- void PROJStringFormatter::addStep(const char *stepName) { d->addStep(); d->steps_.back().name.assign(stepName); } // --------------------------------------------------------------------------- void PROJStringFormatter::addStep(const std::string &stepName) { d->addStep(); d->steps_.back().name = stepName; } // --------------------------------------------------------------------------- void PROJStringFormatter::setCurrentStepInverted(bool inverted) { assert(!d->steps_.empty()); d->steps_.back().inverted = inverted; } // --------------------------------------------------------------------------- bool PROJStringFormatter::hasParam(const char *paramName) const { if (!d->steps_.empty()) { for (const auto ¶mValue : d->steps_.back().paramValues) { if (paramValue.keyEquals(paramName)) { return true; } } } return false; } // --------------------------------------------------------------------------- void PROJStringFormatter::addNoDefs(bool b) { d->addNoDefs_ = b; } // --------------------------------------------------------------------------- bool PROJStringFormatter::getAddNoDefs() const { return d->addNoDefs_; } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const std::string ¶mName) { if (d->steps_.empty()) { d->addStep(); } d->steps_.back().paramValues.push_back(Step::KeyValue(paramName)); } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const char *paramName, int val) { addParam(std::string(paramName), val); } void PROJStringFormatter::addParam(const std::string ¶mName, int val) { addParam(paramName, internal::toString(val)); } // --------------------------------------------------------------------------- static std::string formatToString(double val) { if (std::abs(val * 10 - std::round(val * 10)) < 1e-8) { // For the purpose of // https://www.epsg-registry.org/export.htm?wkt=urn:ogc:def:crs:EPSG::27561 // Latitude of natural of origin to be properly rounded from 55 grad // to // 49.5 deg val = std::round(val * 10) / 10; } return normalizeSerializedString(internal::toString(val)); } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const char *paramName, double val) { addParam(std::string(paramName), val); } void PROJStringFormatter::addParam(const std::string ¶mName, double val) { addParam(paramName, formatToString(val)); } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const char *paramName, const std::vector &vals) { std::string paramValue; for (size_t i = 0; i < vals.size(); ++i) { if (i > 0) { paramValue += ','; } paramValue += formatToString(vals[i]); } addParam(paramName, paramValue); } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const char *paramName, const char *val) { addParam(std::string(paramName), val); } void PROJStringFormatter::addParam(const char *paramName, const std::string &val) { addParam(std::string(paramName), val); } void PROJStringFormatter::addParam(const std::string ¶mName, const char *val) { addParam(paramName, std::string(val)); } // --------------------------------------------------------------------------- void PROJStringFormatter::addParam(const std::string ¶mName, const std::string &val) { if (d->steps_.empty()) { d->addStep(); } d->steps_.back().paramValues.push_back(Step::KeyValue(paramName, val)); } // --------------------------------------------------------------------------- void PROJStringFormatter::setTOWGS84Parameters( const std::vector ¶ms) { d->toWGS84Parameters_ = params; } // --------------------------------------------------------------------------- const std::vector &PROJStringFormatter::getTOWGS84Parameters() const { return d->toWGS84Parameters_; } // --------------------------------------------------------------------------- std::set PROJStringFormatter::getUsedGridNames() const { std::set res; for (const auto &step : d->steps_) { for (const auto ¶m : step.paramValues) { if (param.keyEquals("grids")) { res.insert(param.value); } } } return res; } // --------------------------------------------------------------------------- void PROJStringFormatter::setVDatumExtension(const std::string &filename) { d->vDatumExtension_ = filename; } // --------------------------------------------------------------------------- const std::string &PROJStringFormatter::getVDatumExtension() const { return d->vDatumExtension_; } // --------------------------------------------------------------------------- void PROJStringFormatter::setHDatumExtension(const std::string &filename) { d->hDatumExtension_ = filename; } // --------------------------------------------------------------------------- const std::string &PROJStringFormatter::getHDatumExtension() const { return d->hDatumExtension_; } // --------------------------------------------------------------------------- void PROJStringFormatter::setOmitProjLongLatIfPossible(bool omit) { assert(d->omitProjLongLatIfPossible_ ^ omit); d->omitProjLongLatIfPossible_ = omit; } // --------------------------------------------------------------------------- bool PROJStringFormatter::omitProjLongLatIfPossible() const { return d->omitProjLongLatIfPossible_; } // --------------------------------------------------------------------------- void PROJStringFormatter::pushOmitZUnitConversion() { d->omitZUnitConversion_.push_back(true); } // --------------------------------------------------------------------------- void PROJStringFormatter::popOmitZUnitConversion() { assert(d->omitZUnitConversion_.size() > 1); d->omitZUnitConversion_.pop_back(); } // --------------------------------------------------------------------------- bool PROJStringFormatter::omitZUnitConversion() const { return d->omitZUnitConversion_.back(); } // --------------------------------------------------------------------------- void PROJStringFormatter::setLegacyCRSToCRSContext(bool legacyContext) { d->legacyCRSToCRSContext_ = legacyContext; } // --------------------------------------------------------------------------- bool PROJStringFormatter::getLegacyCRSToCRSContext() const { return d->legacyCRSToCRSContext_; } // --------------------------------------------------------------------------- const DatabaseContextPtr &PROJStringFormatter::databaseContext() const { return d->dbContext_; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct PROJStringParser::Private { DatabaseContextPtr dbContext_{}; PJ_CONTEXT *ctx_{}; bool usePROJ4InitRules_ = false; std::vector warningList_{}; std::string projString_{}; std::vector steps_{}; std::vector globalParamValues_{}; std::string title_{}; bool ignoreNadgrids_ = false; template // cppcheck-suppress functionStatic bool hasParamValue(Step &step, const T key) { for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { pair.usedByParser = true; return true; } } for (auto &pair : step.paramValues) { if (ci_equal(pair.key, key)) { pair.usedByParser = true; return true; } } return false; } template // cppcheck-suppress functionStatic const std::string &getGlobalParamValue(T key) { for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { pair.usedByParser = true; return pair.value; } } return emptyString; } template // cppcheck-suppress functionStatic const std::string &getParamValue(Step &step, const T key) { for (auto &pair : globalParamValues_) { if (ci_equal(pair.key, key)) { pair.usedByParser = true; return pair.value; } } for (auto &pair : step.paramValues) { if (ci_equal(pair.key, key)) { pair.usedByParser = true; return pair.value; } } return emptyString; } static const std::string &getParamValueK(Step &step) { for (auto &pair : step.paramValues) { if (ci_equal(pair.key, "k") || ci_equal(pair.key, "k_0")) { pair.usedByParser = true; return pair.value; } } return emptyString; } // cppcheck-suppress functionStatic bool hasUnusedParameters(const Step &step) const { if (steps_.size() == 1) { for (const auto &pair : step.paramValues) { if (pair.key != "no_defs" && !pair.usedByParser) { return true; } } } return false; } // cppcheck-suppress functionStatic std::string guessBodyName(double a); PrimeMeridianNNPtr buildPrimeMeridian(Step &step); GeodeticReferenceFrameNNPtr buildDatum(Step &step, const std::string &title); GeographicCRSNNPtr buildGeographicCRS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis); GeodeticCRSNNPtr buildGeocentricCRS(int iStep, int iUnitConvert); CRSNNPtr buildProjectedCRS(int iStep, GeographicCRSNNPtr geogCRS, int iUnitConvert, int iAxisSwap); CRSNNPtr buildBoundOrCompoundCRSIfNeeded(int iStep, CRSNNPtr crs); UnitOfMeasure buildUnit(Step &step, const std::string &unitsParamName, const std::string &toMeterParamName); enum class AxisType { REGULAR, NORTH_POLE, SOUTH_POLE }; std::vector processAxisSwap(Step &step, const UnitOfMeasure &unit, int iAxisSwap, AxisType axisType, bool ignorePROJAxis); EllipsoidalCSNNPtr buildEllipsoidalCS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis); }; // --------------------------------------------------------------------------- PROJStringParser::PROJStringParser() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PROJStringParser::~PROJStringParser() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Attach a database context, to allow queries in it if needed. */ PROJStringParser & PROJStringParser::attachDatabaseContext(const DatabaseContextPtr &dbContext) { d->dbContext_ = dbContext; return *this; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PROJStringParser &PROJStringParser::attachContext(PJ_CONTEXT *ctx) { d->ctx_ = ctx; return *this; } //! @endcond // --------------------------------------------------------------------------- /** \brief Set how init=epsg:XXXX syntax should be interpreted. * * @param enable When set to true, * init=epsg:XXXX syntax will be allowed and will be interpreted according to * PROJ.4 and PROJ.5 rules, that is geodeticCRS will have longitude, latitude * order and will expect/output coordinates in radians. ProjectedCRS will have * easting, northing axis order (except the ones with Transverse Mercator South * Orientated projection). */ PROJStringParser &PROJStringParser::setUsePROJ4InitRules(bool enable) { d->usePROJ4InitRules_ = enable; return *this; } // --------------------------------------------------------------------------- /** \brief Return the list of warnings found during parsing. */ std::vector PROJStringParser::warningList() const { return d->warningList_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- static const struct LinearUnitDesc { const char *projName; const char *convToMeter; const char *name; int epsgCode; } linearUnitDescs[] = { {"mm", "0.001", "millimetre", 1025}, {"cm", "0.01", "centimetre", 1033}, {"m", "1.0", "metre", 9001}, {"meter", "1.0", "metre", 9001}, // alternative {"metre", "1.0", "metre", 9001}, // alternative {"ft", "0.3048", "foot", 9002}, {"us-ft", "0.3048006096012192", "US survey foot", 9003}, {"fath", "1.8288", "fathom", 9014}, {"kmi", "1852", "nautical mile", 9030}, {"us-ch", "20.11684023368047", "US survey chain", 9033}, {"us-mi", "1609.347218694437", "US survey mile", 9035}, {"km", "1000.0", "kilometre", 9036}, {"ind-ft", "0.30479841", "Indian foot (1937)", 9081}, {"ind-yd", "0.91439523", "Indian yard (1937)", 9085}, {"mi", "1609.344", "Statute mile", 9093}, {"yd", "0.9144", "yard", 9096}, {"ch", "20.1168", "chain", 9097}, {"link", "0.201168", "link", 9098}, {"dm", "0.1", "decimetre", 0}, // no EPSG equivalent {"in", "0.0254", "inch", 0}, // no EPSG equivalent {"us-in", "0.025400050800101", "US survey inch", 0}, // no EPSG equivalent {"us-yd", "0.914401828803658", "US survey yard", 0}, // no EPSG equivalent {"ind-ch", "20.11669506", "Indian chain", 0}, // no EPSG equivalent }; static const LinearUnitDesc *getLinearUnits(const std::string &projName) { for (const auto &desc : linearUnitDescs) { if (desc.projName == projName) return &desc; } return nullptr; } static const LinearUnitDesc *getLinearUnits(double toMeter) { for (const auto &desc : linearUnitDescs) { if (std::fabs(c_locale_stod(desc.convToMeter) - toMeter) < 1e-10 * toMeter) { return &desc; } } return nullptr; } // --------------------------------------------------------------------------- static UnitOfMeasure _buildUnit(const LinearUnitDesc *unitsMatch) { std::string unitsCode; if (unitsMatch->epsgCode) { std::ostringstream buffer; buffer.imbue(std::locale::classic()); buffer << unitsMatch->epsgCode; unitsCode = buffer.str(); } return UnitOfMeasure( unitsMatch->name, c_locale_stod(unitsMatch->convToMeter), UnitOfMeasure::Type::LINEAR, unitsMatch->epsgCode ? Identifier::EPSG : std::string(), unitsCode); } // --------------------------------------------------------------------------- static UnitOfMeasure _buildUnit(double to_meter_value) { // TODO: look-up in EPSG catalog if (to_meter_value == 0) { throw ParsingException("invalid unit value"); } return UnitOfMeasure("unknown", to_meter_value, UnitOfMeasure::Type::LINEAR); } // --------------------------------------------------------------------------- UnitOfMeasure PROJStringParser::Private::buildUnit(Step &step, const std::string &unitsParamName, const std::string &toMeterParamName) { UnitOfMeasure unit = UnitOfMeasure::METRE; const LinearUnitDesc *unitsMatch = nullptr; const auto &projUnits = getParamValue(step, unitsParamName); if (!projUnits.empty()) { unitsMatch = getLinearUnits(projUnits); if (unitsMatch == nullptr) { throw ParsingException("unhandled " + unitsParamName + "=" + projUnits); } } const auto &toMeter = getParamValue(step, toMeterParamName); if (!toMeter.empty()) { double to_meter_value; try { to_meter_value = c_locale_stod(toMeter); } catch (const std::invalid_argument &) { throw ParsingException("invalid value for " + toMeterParamName); } unitsMatch = getLinearUnits(to_meter_value); if (unitsMatch == nullptr) { unit = _buildUnit(to_meter_value); } } if (unitsMatch) { unit = _buildUnit(unitsMatch); } return unit; } // --------------------------------------------------------------------------- static const struct DatumDesc { const char *projName; const char *gcsName; int gcsCode; const char *datumName; int datumCode; const char *ellipsoidName; int ellipsoidCode; double a; double rf; } datumDescs[] = { {"GGRS87", "GGRS87", 4121, "Greek Geodetic Reference System 1987", 6121, "GRS 1980", 7019, 6378137, 298.257222101}, {"potsdam", "DHDN", 4314, "Deutsches Hauptdreiecksnetz", 6314, "Bessel 1841", 7004, 6377397.155, 299.1528128}, {"carthage", "Carthage", 4223, "Carthage", 6223, "Clarke 1880 (IGN)", 7011, 6378249.2, 293.4660213}, {"hermannskogel", "MGI", 4312, "Militar-Geographische Institut", 6312, "Bessel 1841", 7004, 6377397.155, 299.1528128}, {"ire65", "TM65", 4299, "TM65", 6299, "Airy Modified 1849", 7002, 6377340.189, 299.3249646}, {"nzgd49", "NZGD49", 4272, "New Zealand Geodetic Datum 1949", 6272, "International 1924", 7022, 6378388, 297}, {"OSGB36", "OSGB 1936", 4277, "OSGB 1936", 6277, "Airy 1830", 7001, 6377563.396, 299.3249646}, }; // --------------------------------------------------------------------------- static bool isGeographicStep(const std::string &name) { return name == "longlat" || name == "lonlat" || name == "latlong" || name == "latlon"; } // --------------------------------------------------------------------------- static bool isGeocentricStep(const std::string &name) { return name == "geocent" || name == "cart"; } // --------------------------------------------------------------------------- static bool isProjectedStep(const std::string &name) { if (name == "etmerc" || name == "utm" || !getMappingsFromPROJName(name).empty()) { return true; } // IMPROVE ME: have a better way of distinguishing projections from // other // transformations. if (name == "pipeline" || name == "geoc" || name == "deformation" || name == "helmert" || name == "hgridshift" || name == "molodensky" || name == "vgridshit") { return false; } const auto *operations = proj_list_operations(); for (int i = 0; operations[i].id != nullptr; ++i) { if (name == operations[i].id) { return true; } } return false; } // --------------------------------------------------------------------------- static PropertyMap createMapWithUnknownName() { return PropertyMap().set(common::IdentifiedObject::NAME_KEY, "unknown"); } // --------------------------------------------------------------------------- PrimeMeridianNNPtr PROJStringParser::Private::buildPrimeMeridian(Step &step) { PrimeMeridianNNPtr pm = PrimeMeridian::GREENWICH; const auto &pmStr = getParamValue(step, "pm"); if (!pmStr.empty()) { char *end; double pmValue = dmstor(pmStr.c_str(), &end) * RAD_TO_DEG; if (pmValue != HUGE_VAL && *end == '\0') { pm = PrimeMeridian::create(createMapWithUnknownName(), Angle(pmValue)); } else { bool found = false; if (pmStr == "paris") { found = true; pm = PrimeMeridian::PARIS; } auto proj_prime_meridians = proj_list_prime_meridians(); for (int i = 0; !found && proj_prime_meridians[i].id != nullptr; i++) { if (pmStr == proj_prime_meridians[i].id) { found = true; std::string name = static_cast(::toupper(pmStr[0])) + pmStr.substr(1); pmValue = dmstor(proj_prime_meridians[i].defn, nullptr) * RAD_TO_DEG; pm = PrimeMeridian::create( PropertyMap().set(IdentifiedObject::NAME_KEY, name), Angle(pmValue)); break; } } if (!found) { throw ParsingException("unknown pm " + pmStr); } } } return pm; } // --------------------------------------------------------------------------- std::string PROJStringParser::Private::guessBodyName(double a) { return Ellipsoid::guessBodyName(dbContext_, a); } // --------------------------------------------------------------------------- GeodeticReferenceFrameNNPtr PROJStringParser::Private::buildDatum(Step &step, const std::string &title) { const auto &ellpsStr = getParamValue(step, "ellps"); const auto &datumStr = getParamValue(step, "datum"); const auto &RStr = getParamValue(step, "R"); const auto &aStr = getParamValue(step, "a"); const auto &bStr = getParamValue(step, "b"); const auto &rfStr = getParamValue(step, "rf"); const auto &fStr = getParamValue(step, "f"); const auto &esStr = getParamValue(step, "es"); const auto &eStr = getParamValue(step, "e"); double a = -1.0; double b = -1.0; double rf = -1.0; const util::optional optionalEmptyString{}; const bool numericParamPresent = !RStr.empty() || !aStr.empty() || !bStr.empty() || !rfStr.empty() || !fStr.empty() || !esStr.empty() || !eStr.empty(); PrimeMeridianNNPtr pm(buildPrimeMeridian(step)); PropertyMap grfMap; // It is arguable that we allow the prime meridian of a datum defined by // its name to be overridden, but this is found at least in a regression // test // of GDAL. So let's keep the ellipsoid part of the datum in that case and // use the specified prime meridian. const auto overridePmIfNeeded = [&pm](const GeodeticReferenceFrameNNPtr &grf) { if (pm->_isEquivalentTo(PrimeMeridian::GREENWICH.get())) { return grf; } else { return GeodeticReferenceFrame::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "Unknown based on " + grf->ellipsoid()->nameStr() + " ellipsoid"), grf->ellipsoid(), grf->anchorDefinition(), pm); } }; // R take precedence if (!RStr.empty()) { double R; try { R = c_locale_stod(RStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid R value"); } auto ellipsoid = Ellipsoid::createSphere(createMapWithUnknownName(), Length(R), guessBodyName(R)); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } if (!datumStr.empty()) { auto l_datum = [&datumStr, &overridePmIfNeeded, &grfMap, &optionalEmptyString, &pm]() { if (datumStr == "WGS84") { return overridePmIfNeeded(GeodeticReferenceFrame::EPSG_6326); } else if (datumStr == "NAD83") { return overridePmIfNeeded(GeodeticReferenceFrame::EPSG_6269); } else if (datumStr == "NAD27") { return overridePmIfNeeded(GeodeticReferenceFrame::EPSG_6267); } else { for (const auto &datumDesc : datumDescs) { if (datumStr == datumDesc.projName) { (void)datumDesc.gcsName; // to please cppcheck (void)datumDesc.gcsCode; // to please cppcheck auto ellipsoid = Ellipsoid::createFlattenedSphere( grfMap .set(IdentifiedObject::NAME_KEY, datumDesc.ellipsoidName) .set(Identifier::CODESPACE_KEY, Identifier::EPSG) .set(Identifier::CODE_KEY, datumDesc.ellipsoidCode), Length(datumDesc.a), Scale(datumDesc.rf)); return GeodeticReferenceFrame::create( grfMap .set(IdentifiedObject::NAME_KEY, datumDesc.datumName) .set(Identifier::CODESPACE_KEY, Identifier::EPSG) .set(Identifier::CODE_KEY, datumDesc.datumCode), ellipsoid, optionalEmptyString, pm); } } } throw ParsingException("unknown datum " + datumStr); }(); if (!numericParamPresent) { return l_datum; } a = l_datum->ellipsoid()->semiMajorAxis().getSIValue(); rf = l_datum->ellipsoid()->computedInverseFlattening(); } else if (!ellpsStr.empty()) { auto l_datum = [&ellpsStr, &title, &grfMap, &optionalEmptyString, &pm]() { if (ellpsStr == "WGS84") { return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "Unknown based on WGS84 ellipsoid" : title.c_str()), Ellipsoid::WGS84, optionalEmptyString, pm); } else if (ellpsStr == "GRS80") { return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "Unknown based on GRS80 ellipsoid" : title.c_str()), Ellipsoid::GRS1980, optionalEmptyString, pm); } else { auto proj_ellps = proj_list_ellps(); for (int i = 0; proj_ellps[i].id != nullptr; i++) { if (ellpsStr == proj_ellps[i].id) { assert(strncmp(proj_ellps[i].major, "a=", 2) == 0); const double a_iter = c_locale_stod(proj_ellps[i].major + 2); EllipsoidPtr ellipsoid; PropertyMap ellpsMap; if (strncmp(proj_ellps[i].ell, "b=", 2) == 0) { const double b_iter = c_locale_stod(proj_ellps[i].ell + 2); ellipsoid = Ellipsoid::createTwoAxis( ellpsMap.set(IdentifiedObject::NAME_KEY, proj_ellps[i].name), Length(a_iter), Length(b_iter)) .as_nullable(); } else { assert(strncmp(proj_ellps[i].ell, "rf=", 3) == 0); const double rf_iter = c_locale_stod(proj_ellps[i].ell + 3); ellipsoid = Ellipsoid::createFlattenedSphere( ellpsMap.set(IdentifiedObject::NAME_KEY, proj_ellps[i].name), Length(a_iter), Scale(rf_iter)) .as_nullable(); } return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? std::string("Unknown based on ") + proj_ellps[i].name + " ellipsoid" : title), NN_NO_CHECK(ellipsoid), optionalEmptyString, pm); } } throw ParsingException("unknown ellipsoid " + ellpsStr); } }(); if (!numericParamPresent) { return l_datum; } a = l_datum->ellipsoid()->semiMajorAxis().getSIValue(); if (l_datum->ellipsoid()->semiMinorAxis().has_value()) { b = l_datum->ellipsoid()->semiMinorAxis()->getSIValue(); } else { rf = l_datum->ellipsoid()->computedInverseFlattening(); } } if (!aStr.empty()) { try { a = c_locale_stod(aStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid a value"); } } if (a > 0 && (b > 0 || !bStr.empty())) { if (!bStr.empty()) { try { b = c_locale_stod(bStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid b value"); } } auto ellipsoid = Ellipsoid::createTwoAxis(createMapWithUnknownName(), Length(a), Length(b), guessBodyName(a)) ->identify(); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } else if (a > 0 && (rf >= 0 || !rfStr.empty())) { if (!rfStr.empty()) { try { rf = c_locale_stod(rfStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid rf value"); } } auto ellipsoid = Ellipsoid::createFlattenedSphere( createMapWithUnknownName(), Length(a), Scale(rf), guessBodyName(a)) ->identify(); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } else if (a > 0 && !fStr.empty()) { double f; try { f = c_locale_stod(fStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid f value"); } auto ellipsoid = Ellipsoid::createFlattenedSphere( createMapWithUnknownName(), Length(a), Scale(f != 0.0 ? 1.0 / f : 0.0), guessBodyName(a)) ->identify(); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } else if (a > 0 && !eStr.empty()) { double e; try { e = c_locale_stod(eStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid e value"); } double alpha = asin(e); /* angular eccentricity */ double f = 1 - cos(alpha); /* = 1 - sqrt (1 - es); */ auto ellipsoid = Ellipsoid::createFlattenedSphere( createMapWithUnknownName(), Length(a), Scale(f != 0.0 ? 1.0 / f : 0.0), guessBodyName(a)) ->identify(); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } else if (a > 0 && !esStr.empty()) { double es; try { es = c_locale_stod(esStr); } catch (const std::invalid_argument &) { throw ParsingException("Invalid es value"); } double f = 1 - sqrt(1 - es); auto ellipsoid = Ellipsoid::createFlattenedSphere( createMapWithUnknownName(), Length(a), Scale(f != 0.0 ? 1.0 / f : 0.0), guessBodyName(a)) ->identify(); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } // If only a is specified, create a sphere if (a > 0 && bStr.empty() && rfStr.empty() && eStr.empty() && esStr.empty()) { auto ellipsoid = Ellipsoid::createSphere(createMapWithUnknownName(), Length(a), guessBodyName(a)); return GeodeticReferenceFrame::create( grfMap.set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title.c_str()), ellipsoid, optionalEmptyString, fixupPrimeMeridan(ellipsoid, pm)); } if (!bStr.empty() && aStr.empty()) { throw ParsingException("b found, but a missing"); } if (!rfStr.empty() && aStr.empty()) { throw ParsingException("rf found, but a missing"); } if (!fStr.empty() && aStr.empty()) { throw ParsingException("f found, but a missing"); } if (!eStr.empty() && aStr.empty()) { throw ParsingException("e found, but a missing"); } if (!esStr.empty() && aStr.empty()) { throw ParsingException("es found, but a missing"); } return overridePmIfNeeded(GeodeticReferenceFrame::EPSG_6326); } // --------------------------------------------------------------------------- static const MeridianPtr nullMeridian{}; static CoordinateSystemAxisNNPtr createAxis(const std::string &name, const std::string &abbreviation, const AxisDirection &direction, const common::UnitOfMeasure &unit, const MeridianPtr &meridian = nullMeridian) { return CoordinateSystemAxis::create( PropertyMap().set(IdentifiedObject::NAME_KEY, name), abbreviation, direction, unit, meridian); } std::vector PROJStringParser::Private::processAxisSwap(Step &step, const UnitOfMeasure &unit, int iAxisSwap, AxisType axisType, bool ignorePROJAxis) { assert(iAxisSwap < 0 || ci_equal(steps_[iAxisSwap].name, "axisswap")); const bool isGeographic = unit.type() == UnitOfMeasure::Type::ANGULAR; const auto &eastName = isGeographic ? AxisName::Longitude : AxisName::Easting; const auto &eastAbbev = isGeographic ? AxisAbbreviation::lon : AxisAbbreviation::E; const auto &eastDir = isGeographic ? AxisDirection::EAST : (axisType == AxisType::NORTH_POLE) ? AxisDirection::SOUTH : (axisType == AxisType::SOUTH_POLE) ? AxisDirection::NORTH : AxisDirection::EAST; CoordinateSystemAxisNNPtr east = createAxis( eastName, eastAbbev, eastDir, unit, (!isGeographic && (axisType == AxisType::NORTH_POLE || axisType == AxisType::SOUTH_POLE)) ? Meridian::create(Angle(90, UnitOfMeasure::DEGREE)).as_nullable() : nullMeridian); const auto &northName = isGeographic ? AxisName::Latitude : AxisName::Northing; const auto &northAbbev = isGeographic ? AxisAbbreviation::lat : AxisAbbreviation::N; const auto &northDir = isGeographic ? AxisDirection::NORTH : (axisType == AxisType::NORTH_POLE) ? AxisDirection::SOUTH /*: (axisType == AxisType::SOUTH_POLE) ? AxisDirection::NORTH*/ : AxisDirection::NORTH; CoordinateSystemAxisNNPtr north = createAxis( northName, northAbbev, northDir, unit, (!isGeographic && axisType == AxisType::NORTH_POLE) ? Meridian::create(Angle(180, UnitOfMeasure::DEGREE)).as_nullable() : (!isGeographic && axisType == AxisType::SOUTH_POLE) ? Meridian::create(Angle(0, UnitOfMeasure::DEGREE)) .as_nullable() : nullMeridian); CoordinateSystemAxisNNPtr west = createAxis(isGeographic ? AxisName::Longitude : AxisName::Westing, isGeographic ? AxisAbbreviation::lon : std::string(), AxisDirection::WEST, unit); CoordinateSystemAxisNNPtr south = createAxis(isGeographic ? AxisName::Latitude : AxisName::Southing, isGeographic ? AxisAbbreviation::lat : std::string(), AxisDirection::SOUTH, unit); std::vector axis{east, north}; const auto &axisStr = getParamValue(step, "axis"); if (!ignorePROJAxis && !axisStr.empty()) { if (axisStr.size() == 3) { for (int i = 0; i < 2; i++) { if (axisStr[i] == 'n') { axis[i] = north; } else if (axisStr[i] == 's') { axis[i] = south; } else if (axisStr[i] == 'e') { axis[i] = east; } else if (axisStr[i] == 'w') { axis[i] = west; } else { throw ParsingException("Unhandled axis=" + axisStr); } } } else { throw ParsingException("Unhandled axis=" + axisStr); } } else if (iAxisSwap >= 0) { auto &stepAxisSwap = steps_[iAxisSwap]; const auto &orderStr = getParamValue(stepAxisSwap, "order"); auto orderTab = split(orderStr, ','); if (orderTab.size() != 2) { throw ParsingException("Unhandled order=" + orderStr); } if (stepAxisSwap.inverted) { throw ParsingException("Unhandled +inv for +proj=axisswap"); } for (size_t i = 0; i < 2; i++) { if (orderTab[i] == "1") { axis[i] = east; } else if (orderTab[i] == "-1") { axis[i] = west; } else if (orderTab[i] == "2") { axis[i] = north; } else if (orderTab[i] == "-2") { axis[i] = south; } else { throw ParsingException("Unhandled order=" + orderStr); } } } return axis; } // --------------------------------------------------------------------------- EllipsoidalCSNNPtr PROJStringParser::Private::buildEllipsoidalCS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis) { auto &step = steps_[iStep]; assert(iUnitConvert < 0 || ci_equal(steps_[iUnitConvert].name, "unitconvert")); UnitOfMeasure angularUnit = UnitOfMeasure::DEGREE; if (iUnitConvert >= 0) { auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); if (stepUnitConvert.inverted) { std::swap(xy_in, xy_out); } if (iUnitConvert < iStep) { std::swap(xy_in, xy_out); } if (xy_in->empty() || xy_out->empty() || *xy_in != "rad" || (*xy_out != "rad" && *xy_out != "deg" && *xy_out != "grad")) { throw ParsingException("unhandled values for xy_in and/or xy_out"); } if (*xy_out == "rad") { angularUnit = UnitOfMeasure::RADIAN; } else if (*xy_out == "grad") { angularUnit = UnitOfMeasure::GRAD; } } std::vector axis = processAxisSwap( step, angularUnit, iAxisSwap, AxisType::REGULAR, ignorePROJAxis); CoordinateSystemAxisNNPtr up = CoordinateSystemAxis::create( util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Ellipsoidal_height), AxisAbbreviation::h, AxisDirection::UP, buildUnit(step, "vunits", "vto_meter")); return (!ignoreVUnits && !hasParamValue(step, "geoidgrids") && (hasParamValue(step, "vunits") || hasParamValue(step, "vto_meter"))) ? EllipsoidalCS::create(emptyPropertyMap, axis[0], axis[1], up) : EllipsoidalCS::create(emptyPropertyMap, axis[0], axis[1]); } // --------------------------------------------------------------------------- static double getNumericValue(const std::string ¶mValue, bool *pHasError = nullptr) { try { double value = c_locale_stod(paramValue); if (pHasError) *pHasError = false; return value; } catch (const std::invalid_argument &) { if (pHasError) *pHasError = true; return 0.0; } } // --------------------------------------------------------------------------- namespace { template inline void ignoreRetVal(T) {} } GeographicCRSNNPtr PROJStringParser::Private::buildGeographicCRS(int iStep, int iUnitConvert, int iAxisSwap, bool ignoreVUnits, bool ignorePROJAxis) { auto &step = steps_[iStep]; const bool l_isGeographicStep = isGeographicStep(step.name); const auto &title = l_isGeographicStep ? title_ : emptyString; // units=m is often found in the wild. // No need to create a extension string for this ignoreRetVal(hasParamValue(step, "units")); auto datum = buildDatum(step, title); auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); auto cs = buildEllipsoidalCS(iStep, iUnitConvert, iAxisSwap, ignoreVUnits, ignorePROJAxis); if (l_isGeographicStep && (hasUnusedParameters(step) || getNumericValue(getParamValue(step, "lon_0")) != 0.0)) { props.set("EXTENSION_PROJ4", projString_); } props.set("IMPLICIT_CS", true); return GeographicCRS::create(props, datum, cs); } // --------------------------------------------------------------------------- GeodeticCRSNNPtr PROJStringParser::Private::buildGeocentricCRS(int iStep, int iUnitConvert) { auto &step = steps_[iStep]; assert(isGeocentricStep(step.name)); assert(iUnitConvert < 0 || ci_equal(steps_[iUnitConvert].name, "unitconvert")); const auto &title = title_; auto datum = buildDatum(step, title); UnitOfMeasure unit = buildUnit(step, "units", ""); if (iUnitConvert >= 0) { auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); const std::string *z_in = &getParamValue(stepUnitConvert, "z_in"); const std::string *z_out = &getParamValue(stepUnitConvert, "z_out"); if (stepUnitConvert.inverted) { std::swap(xy_in, xy_out); std::swap(z_in, z_out); } if (xy_in->empty() || xy_out->empty() || *xy_in != "m" || *z_in != "m" || *xy_out != *z_out) { throw ParsingException( "unhandled values for xy_in, z_in, xy_out or z_out"); } const LinearUnitDesc *unitsMatch = nullptr; try { double to_meter_value = c_locale_stod(*xy_out); unitsMatch = getLinearUnits(to_meter_value); if (unitsMatch == nullptr) { unit = _buildUnit(to_meter_value); } } catch (const std::invalid_argument &) { unitsMatch = getLinearUnits(*xy_out); if (!unitsMatch) { throw ParsingException( "unhandled values for xy_in, z_in, xy_out or z_out"); } unit = _buildUnit(unitsMatch); } } auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); auto cs = CartesianCS::createGeocentric(unit); if (hasUnusedParameters(step)) { props.set("EXTENSION_PROJ4", projString_); } return GeodeticCRS::create(props, datum, cs); } // --------------------------------------------------------------------------- CRSNNPtr PROJStringParser::Private::buildBoundOrCompoundCRSIfNeeded(int iStep, CRSNNPtr crs) { auto &step = steps_[iStep]; const auto &nadgrids = getParamValue(step, "nadgrids"); const auto &towgs84 = getParamValue(step, "towgs84"); // nadgrids has the priority over towgs84 if (!ignoreNadgrids_ && !nadgrids.empty()) { crs = BoundCRS::createFromNadgrids(crs, nadgrids); } else if (!towgs84.empty()) { std::vector towgs84Values; const auto tokens = split(towgs84, ','); for (const auto &str : tokens) { try { towgs84Values.push_back(c_locale_stod(str)); } catch (const std::invalid_argument &) { throw ParsingException("Non numerical value in towgs84 clause"); } } crs = BoundCRS::createFromTOWGS84(crs, towgs84Values); } const auto &geoidgrids = getParamValue(step, "geoidgrids"); if (!geoidgrids.empty()) { auto vdatum = VerticalReferenceFrame::create(createMapWithUnknownName()); const UnitOfMeasure unit = buildUnit(step, "vunits", "vto_meter"); auto vcrs = VerticalCRS::create(createMapWithUnknownName(), vdatum, VerticalCS::createGravityRelatedHeight(unit)); auto transformation = Transformation::createGravityRelatedHeightToGeographic3D( PropertyMap().set(IdentifiedObject::NAME_KEY, "unknown to WGS84 ellipsoidal height"), VerticalCRS::create(createMapWithUnknownName(), vdatum, VerticalCS::createGravityRelatedHeight( common::UnitOfMeasure::METRE)), GeographicCRS::EPSG_4979, nullptr, geoidgrids, std::vector()); auto boundvcrs = BoundCRS::create(vcrs, GeographicCRS::EPSG_4979, transformation); crs = CompoundCRS::create(createMapWithUnknownName(), std::vector{crs, boundvcrs}); } return crs; } // --------------------------------------------------------------------------- static double getAngularValue(const std::string ¶mValue, bool *pHasError = nullptr) { char *endptr = nullptr; double value = dmstor(paramValue.c_str(), &endptr) * RAD_TO_DEG; if (value == HUGE_VAL || endptr != paramValue.c_str() + paramValue.size()) { if (pHasError) *pHasError = true; return 0.0; } if (pHasError) *pHasError = false; return value; } // --------------------------------------------------------------------------- static bool is_in_stringlist(const std::string &str, const char *stringlist) { if (str.empty()) return false; const char *haystack = stringlist; while (true) { const char *res = strstr(haystack, str.c_str()); if (res == nullptr) return false; if ((res == stringlist || res[-1] == ',') && (res[str.size()] == ',' || res[str.size()] == '\0')) return true; haystack += str.size(); } } // --------------------------------------------------------------------------- CRSNNPtr PROJStringParser::Private::buildProjectedCRS( int iStep, GeographicCRSNNPtr geogCRS, int iUnitConvert, int iAxisSwap) { auto &step = steps_[iStep]; auto mappings = getMappingsFromPROJName(step.name); const MethodMapping *mapping = mappings.empty() ? nullptr : mappings[0]; assert(isProjectedStep(step.name)); assert(iUnitConvert < 0 || ci_equal(steps_[iUnitConvert].name, "unitconvert")); const auto &title = title_; if (!buildPrimeMeridian(step)->longitude()._isEquivalentTo( geogCRS->primeMeridian()->longitude(), util::IComparable::Criterion::EQUIVALENT)) { throw ParsingException("inconsistent pm values between projectedCRS " "and its base geographicalCRS"); } auto axisType = AxisType::REGULAR; bool bWebMercator = false; if (step.name == "tmerc" && ((getParamValue(step, "axis") == "wsu" && iAxisSwap < 0) || (iAxisSwap > 0 && getParamValue(steps_[iAxisSwap], "order") == "-1,-2"))) { mapping = getMapping(EPSG_CODE_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED); } else if (step.name == "etmerc") { mapping = getMapping(EPSG_CODE_METHOD_TRANSVERSE_MERCATOR); } else if (step.name == "lcc") { const auto &lat_0 = getParamValue(step, "lat_0"); const auto &lat_1 = getParamValue(step, "lat_1"); const auto &lat_2 = getParamValue(step, "lat_2"); const auto &k = getParamValueK(step); if (lat_2.empty() && !lat_0.empty() && !lat_1.empty() && (lat_0 == lat_1 || // For some reason with gcc 5.3.1-14ubuntu2 32bit, the following // comparison returns false even if lat_0 == lat_1. Smells like // a compiler bug getAngularValue(lat_0) == getAngularValue(lat_1))) { mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); } else if (!k.empty() && getNumericValue(k) != 1.0) { mapping = getMapping( EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN); } else { mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP); } } else if (step.name == "aeqd" && hasParamValue(step, "guam")) { mapping = getMapping(EPSG_CODE_METHOD_GUAM_PROJECTION); } else if (step.name == "cea" && !geogCRS->ellipsoid()->isSphere()) { mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA); } else if (step.name == "geos" && getParamValue(step, "sweep") == "x") { mapping = getMapping(PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X); } else if (step.name == "geos") { mapping = getMapping(PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_Y); } else if (step.name == "omerc") { if (hasParamValue(step, "no_rot")) { mapping = nullptr; } else if (hasParamValue(step, "no_uoff") || hasParamValue(step, "no_off")) { mapping = getMapping(EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A); } else if (hasParamValue(step, "lat_1") && hasParamValue(step, "lon_1") && hasParamValue(step, "lat_2") && hasParamValue(step, "lon_2")) { mapping = getMapping( PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN); } else { mapping = getMapping(EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B); } } else if (step.name == "somerc") { mapping = getMapping(EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B); if (!hasParamValue(step, "alpha") && !hasParamValue(step, "gamma") && !hasParamValue(step, "lonc")) { step.paramValues.emplace_back(Step::KeyValue("alpha", "90")); step.paramValues.emplace_back(Step::KeyValue("gamma", "90")); step.paramValues.emplace_back( Step::KeyValue("lonc", getParamValue(step, "lon_0"))); } } else if (step.name == "krovak" && ((getParamValue(step, "axis") == "swu" && iAxisSwap < 0) || (iAxisSwap > 0 && getParamValue(steps_[iAxisSwap], "order") == "-2,-1"))) { mapping = getMapping(EPSG_CODE_METHOD_KROVAK); } else if (step.name == "merc") { if (hasParamValue(step, "a") && hasParamValue(step, "b") && getParamValue(step, "a") == getParamValue(step, "b") && (!hasParamValue(step, "lat_ts") || getAngularValue(getParamValue(step, "lat_ts")) == 0.0) && getNumericValue(getParamValueK(step)) == 1.0 && getParamValue(step, "nadgrids") == "@null") { mapping = getMapping( EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR); for (size_t i = 0; i < step.paramValues.size(); ++i) { if (ci_equal(step.paramValues[i].key, "nadgrids")) { ignoreNadgrids_ = true; break; } } if (getNumericValue(getParamValue(step, "a")) == 6378137 && getAngularValue(getParamValue(step, "lon_0")) == 0.0 && getAngularValue(getParamValue(step, "lat_0")) == 0.0 && getAngularValue(getParamValue(step, "x_0")) == 0.0 && getAngularValue(getParamValue(step, "y_0")) == 0.0) { bWebMercator = true; } } else if (hasParamValue(step, "lat_ts")) { mapping = getMapping(EPSG_CODE_METHOD_MERCATOR_VARIANT_B); } else { mapping = getMapping(EPSG_CODE_METHOD_MERCATOR_VARIANT_A); } } else if (step.name == "stere") { if (hasParamValue(step, "lat_0") && std::fabs(std::fabs(getAngularValue(getParamValue(step, "lat_0"))) - 90.0) < 1e-10) { const double lat_0 = getAngularValue(getParamValue(step, "lat_0")); if (lat_0 > 0) { axisType = AxisType::NORTH_POLE; } else { axisType = AxisType::SOUTH_POLE; } const auto &lat_ts = getParamValue(step, "lat_ts"); const auto &k = getParamValueK(step); if (!lat_ts.empty() && std::fabs(getAngularValue(lat_ts) - lat_0) > 1e-10 && !k.empty() && std::fabs(getNumericValue(k) - 1) > 1e-10) { throw ParsingException("lat_ts != lat_0 and k != 1 not " "supported for Polar Stereographic"); } if (!lat_ts.empty() && (k.empty() || std::fabs(getNumericValue(k) - 1) < 1e-10)) { mapping = getMapping(EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B); } else { mapping = getMapping(EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A); } } else { mapping = getMapping(PROJ_WKT2_NAME_METHOD_STEREOGRAPHIC); } } else if (step.name == "laea") { if (hasParamValue(step, "lat_0") && std::fabs(std::fabs(getAngularValue(getParamValue(step, "lat_0"))) - 90.0) < 1e-10) { const double lat_0 = getAngularValue(getParamValue(step, "lat_0")); if (lat_0 > 0) { axisType = AxisType::NORTH_POLE; } else { axisType = AxisType::SOUTH_POLE; } } if (geogCRS->ellipsoid()->isSphere()) { mapping = getMapping( EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL); } } else if (step.name == "eqc") { if (geogCRS->ellipsoid()->isSphere()) { mapping = getMapping(EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL); } } UnitOfMeasure unit = buildUnit(step, "units", "to_meter"); if (iUnitConvert >= 0) { auto &stepUnitConvert = steps_[iUnitConvert]; const std::string *xy_in = &getParamValue(stepUnitConvert, "xy_in"); const std::string *xy_out = &getParamValue(stepUnitConvert, "xy_out"); if (stepUnitConvert.inverted) { std::swap(xy_in, xy_out); } if (xy_in->empty() || xy_out->empty() || *xy_in != "m") { if (step.name != "ob_tran") { throw ParsingException( "unhandled values for xy_in and/or xy_out"); } } const LinearUnitDesc *unitsMatch = nullptr; try { double to_meter_value = c_locale_stod(*xy_out); unitsMatch = getLinearUnits(to_meter_value); if (unitsMatch == nullptr) { unit = _buildUnit(to_meter_value); } } catch (const std::invalid_argument &) { unitsMatch = getLinearUnits(*xy_out); if (!unitsMatch) { if (step.name != "ob_tran") { throw ParsingException( "unhandled values for xy_in and/or xy_out"); } } else { unit = _buildUnit(unitsMatch); } } } ConversionPtr conv; auto mapWithUnknownName = createMapWithUnknownName(); if (step.name == "utm") { const int zone = std::atoi(getParamValue(step, "zone").c_str()); const bool north = !hasParamValue(step, "south"); conv = Conversion::createUTM(emptyPropertyMap, zone, north).as_nullable(); } else if (mapping) { auto methodMap = PropertyMap().set(IdentifiedObject::NAME_KEY, mapping->wkt2_name); if (mapping->epsg_code) { methodMap.set(Identifier::CODESPACE_KEY, Identifier::EPSG) .set(Identifier::CODE_KEY, mapping->epsg_code); } std::vector parameters; std::vector values; for (int i = 0; mapping->params[i] != nullptr; i++) { const auto *param = mapping->params[i]; std::string proj_name(param->proj_name ? param->proj_name : ""); const std::string *paramValue = (proj_name == "k" || proj_name == "k_0") ? &getParamValueK(step) : !proj_name.empty() ? &getParamValue(step, proj_name) : &emptyString; double value = 0; if (!paramValue->empty()) { bool hasError = false; if (param->unit_type == UnitOfMeasure::Type::ANGULAR) { value = getAngularValue(*paramValue, &hasError); } else { value = getNumericValue(*paramValue, &hasError); } if (hasError) { throw ParsingException("invalid value for " + proj_name); } } else if (param->unit_type == UnitOfMeasure::Type::SCALE) { value = 1; } // For omerc, if gamma is missing, the default value is // alpha else if (step.name == "omerc" && proj_name == "gamma") { paramValue = &getParamValue(step, "alpha"); if (!paramValue->empty()) { value = getAngularValue(*paramValue); } } else if (step.name == "krovak") { if (param->epsg_code == EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS) { value = 30.28813975277777776; } else if ( param->epsg_code == EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL) { value = 78.5; } } else if (step.name == "cea" && proj_name == "lat_ts") { paramValue = &getParamValueK(step); if (!paramValue->empty()) { bool hasError = false; const double k = getNumericValue(*paramValue, &hasError); if (hasError) { throw ParsingException("invalid value for k/k_0"); } if (k >= 0 && k <= 1) { const double es = geogCRS->ellipsoid()->squaredEccentricity(); if (es < 0) { throw ParsingException("Invalid flattening"); } value = Angle(acos(k * sqrt((1 - es) / (1 - k * k * es))), UnitOfMeasure::RADIAN) .convertToUnit(UnitOfMeasure::DEGREE); } else { throw ParsingException("k/k_0 should be in [0,1]"); } } } PropertyMap propertiesParameter; propertiesParameter.set(IdentifiedObject::NAME_KEY, param->wkt2_name); if (param->epsg_code) { propertiesParameter.set(Identifier::CODE_KEY, param->epsg_code); propertiesParameter.set(Identifier::CODESPACE_KEY, Identifier::EPSG); } parameters.push_back( OperationParameter::create(propertiesParameter)); // In PROJ convention, angular parameters are always in degree // and linear parameters always in metre. double valRounded = param->unit_type == UnitOfMeasure::Type::LINEAR ? Length(value, UnitOfMeasure::METRE).convertToUnit(unit) : value; if (std::fabs(valRounded - std::round(valRounded)) < 1e-8) { valRounded = std::round(valRounded); } values.push_back(ParameterValue::create(Measure( valRounded, param->unit_type == UnitOfMeasure::Type::ANGULAR ? UnitOfMeasure::DEGREE : param->unit_type == UnitOfMeasure::Type::LINEAR ? unit : param->unit_type == UnitOfMeasure::Type::SCALE ? UnitOfMeasure::SCALE_UNITY : UnitOfMeasure::NONE))); } if (step.name == "tmerc" && hasParamValue(step, "approx")) { methodMap.set("proj_method", "tmerc approx"); } else if (step.name == "utm" && hasParamValue(step, "approx")) { methodMap.set("proj_method", "utm approx"); } conv = Conversion::create(mapWithUnknownName, methodMap, parameters, values) .as_nullable(); } else { std::vector parameters; std::vector values; std::string methodName = "PROJ " + step.name; for (const auto ¶m : step.paramValues) { if (is_in_stringlist(param.key, "wktext,no_defs,datum,ellps,a,b,R,towgs84," "nadgrids,geoidgrids," "units,to_meter,vunits,vto_meter,type")) { continue; } if (param.value.empty()) { methodName += " " + param.key; } else if (isalpha(param.value[0])) { methodName += " " + param.key + "=" + param.value; } else { parameters.push_back(OperationParameter::create( PropertyMap().set(IdentifiedObject::NAME_KEY, param.key))); bool hasError = false; if (is_in_stringlist(param.key, "x_0,y_0,h,h_0")) { double value = getNumericValue(param.value, &hasError); values.push_back(ParameterValue::create( Measure(value, UnitOfMeasure::METRE))); } else if (is_in_stringlist( param.key, "k,k_0," "north_square,south_square," // rhealpix "n,m," // sinu "q," // urm5 "path,lsat," // lsat "W,M," // hammer "aperture,resolution," // isea )) { double value = getNumericValue(param.value, &hasError); values.push_back(ParameterValue::create( Measure(value, UnitOfMeasure::SCALE_UNITY))); } else { double value = getAngularValue(param.value, &hasError); values.push_back(ParameterValue::create( Measure(value, UnitOfMeasure::DEGREE))); } if (hasError) { throw ParsingException("invalid value for " + param.key); } } } conv = Conversion::create( mapWithUnknownName, PropertyMap().set(IdentifiedObject::NAME_KEY, methodName), parameters, values) .as_nullable(); if (is_in_stringlist(methodName, "PROJ ob_tran o_proj=longlat," "PROJ ob_tran o_proj=lonlat," "PROJ ob_tran o_proj=latlon," "PROJ ob_tran o_proj=latlong")) { return DerivedGeographicCRS::create( PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), geogCRS, NN_NO_CHECK(conv), buildEllipsoidalCS(iStep, iUnitConvert, iAxisSwap, false, false)); } } std::vector axis = processAxisSwap(step, unit, iAxisSwap, axisType, false); auto cs = CartesianCS::create(emptyPropertyMap, axis[0], axis[1]); auto props = PropertyMap().set(IdentifiedObject::NAME_KEY, title.empty() ? "unknown" : title); if (hasUnusedParameters(step)) { props.set("EXTENSION_PROJ4", projString_); } props.set("IMPLICIT_CS", true); CRSNNPtr crs = bWebMercator ? createPseudoMercator(props.set(IdentifiedObject::NAME_KEY, "WGS 84 / Pseudo-Mercator")) : ProjectedCRS::create(props, geogCRS, NN_NO_CHECK(conv), cs); if (!hasParamValue(step, "geoidgrids") && (hasParamValue(step, "vunits") || hasParamValue(step, "vto_meter"))) { auto vdatum = VerticalReferenceFrame::create(mapWithUnknownName); const UnitOfMeasure vunit = buildUnit(step, "vunits", "vto_meter"); auto vcrs = VerticalCRS::create(mapWithUnknownName, vdatum, VerticalCS::createGravityRelatedHeight(vunit)); crs = CompoundCRS::create(mapWithUnknownName, std::vector{crs, vcrs}); } return crs; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const metadata::ExtentPtr nullExtent{}; static const metadata::ExtentPtr &getExtent(const crs::CRS *crs) { const auto &domains = crs->domains(); if (!domains.empty()) { return domains[0]->domainOfValidity(); } return nullExtent; } //! @endcond namespace { struct PJContextHolder { PJ_CONTEXT *ctx_; bool bFree_; PJContextHolder(PJ_CONTEXT *ctx, bool bFree) : ctx_(ctx), bFree_(bFree) {} ~PJContextHolder() { if (bFree_) proj_context_destroy(ctx_); } PJContextHolder(const PJContextHolder &) = delete; PJContextHolder &operator=(const PJContextHolder &) = delete; }; } // namespace // --------------------------------------------------------------------------- /** \brief Instantiate a sub-class of BaseObject from a PROJ string. * * The projString must contain +type=crs for the object to be detected as a * CRS instead of a CoordinateOperation. * * @throw ParsingException */ BaseObjectNNPtr PROJStringParser::createFromPROJString(const std::string &projString) { // In some abnormal situations involving init=epsg:XXXX syntax, we could // have infinite loop if (d->ctx_ && d->ctx_->projStringParserCreateFromPROJStringRecursionCounter == 2) { throw ParsingException( "Infinite recursion in PROJStringParser::createFromPROJString()"); } d->steps_.clear(); d->title_.clear(); d->globalParamValues_.clear(); d->projString_ = projString; PROJStringSyntaxParser(projString, d->steps_, d->globalParamValues_, d->title_); if (d->steps_.empty()) { const auto &vunits = d->getGlobalParamValue("vunits"); const auto &vto_meter = d->getGlobalParamValue("vto_meter"); if (!vunits.empty() || !vto_meter.empty()) { Step fakeStep; if (!vunits.empty()) { fakeStep.paramValues.emplace_back( Step::KeyValue("vunits", vunits)); } if (!vto_meter.empty()) { fakeStep.paramValues.emplace_back( Step::KeyValue("vto_meter", vto_meter)); } auto vdatum = VerticalReferenceFrame::create(createMapWithUnknownName()); auto vcrs = VerticalCRS::create( createMapWithUnknownName(), vdatum, VerticalCS::createGravityRelatedHeight( d->buildUnit(fakeStep, "vunits", "vto_meter"))); return vcrs; } } const bool isGeocentricCRS = ((d->steps_.size() == 1 && d->getParamValue(d->steps_[0], "type") == "crs") || (d->steps_.size() == 2 && d->steps_[1].name == "unitconvert")) && !d->steps_[0].inverted && isGeocentricStep(d->steps_[0].name); // +init=xxxx:yyyy syntax if (d->steps_.size() == 1 && d->steps_[0].isInit && !d->steps_[0].inverted) { // Those used to come from a text init file // We only support them in compatibility mode const std::string &stepName = d->steps_[0].name; if (ci_starts_with(stepName, "epsg:") || ci_starts_with(stepName, "IGNF:")) { /* We create a new context so as to avoid messing up with the */ /* errorno of the main context, when trying to find the likely */ /* missing epsg file */ auto ctx = proj_context_create(); if (!ctx) { throw ParsingException("out of memory"); } PJContextHolder contextHolder(ctx, true); if (d->ctx_) { ctx->set_search_paths(d->ctx_->search_paths); ctx->file_finder = d->ctx_->file_finder; ctx->file_finder_legacy = d->ctx_->file_finder_legacy; ctx->file_finder_user_data = d->ctx_->file_finder_user_data; } bool usePROJ4InitRules = d->usePROJ4InitRules_; if (!usePROJ4InitRules) { usePROJ4InitRules = proj_context_get_use_proj4_init_rules(ctx, FALSE) == TRUE; } if (!usePROJ4InitRules) { throw ParsingException("init=epsg:/init=IGNF: syntax not " "supported in non-PROJ4 emulation mode"); } char unused[256]; std::string initname(stepName); initname.resize(initname.find(':')); int file_found = pj_find_file(ctx, initname.c_str(), unused, sizeof(unused)); if (!file_found) { auto obj = createFromUserInput(stepName, d->dbContext_, true); auto crs = dynamic_cast(obj.get()); bool hasSignificantParamValues = false; for (const auto &kv : d->steps_[0].paramValues) { if (!((kv.key == "type" && kv.value == "crs") || kv.key == "no_defs")) { hasSignificantParamValues = true; break; } } if (crs && !hasSignificantParamValues) { PropertyMap properties; properties.set(IdentifiedObject::NAME_KEY, d->title_.empty() ? crs->nameStr() : d->title_); const auto &extent = getExtent(crs); if (extent) { properties.set( common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(extent)); } auto geogCRS = dynamic_cast(crs); if (geogCRS) { // Override with longitude latitude in degrees return GeographicCRS::create( properties, geogCRS->datum(), geogCRS->datumEnsemble(), EllipsoidalCS::createLongitudeLatitude( UnitOfMeasure::DEGREE)); } auto projCRS = dynamic_cast(crs); if (projCRS) { // Override with easting northing order const auto conv = projCRS->derivingConversion(); if (conv->method()->getEPSGCode() != EPSG_CODE_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED) { return ProjectedCRS::create( properties, projCRS->baseCRS(), conv, CartesianCS::createEastingNorthing( projCRS->coordinateSystem() ->axisList()[0] ->unit())); } } return obj; } auto projStringExportable = dynamic_cast(crs); if (projStringExportable) { std::string expanded; if (!d->title_.empty()) { expanded = "title="; expanded += d->title_; } for (const auto &pair : d->steps_[0].paramValues) { if (!expanded.empty()) expanded += ' '; expanded += '+'; expanded += pair.key; if (!pair.value.empty()) { expanded += '='; expanded += pair.value; } } expanded += ' '; expanded += projStringExportable->exportToPROJString( PROJStringFormatter::create().get()); return createFromPROJString(expanded); } } } auto ctx = d->ctx_ ? d->ctx_ : proj_context_create(); if (!ctx) { throw ParsingException("out of memory"); } PJContextHolder contextHolder(ctx, ctx != d->ctx_); paralist *init = pj_mkparam(("init=" + d->steps_[0].name).c_str()); if (!init) { throw ParsingException("out of memory"); } ctx->projStringParserCreateFromPROJStringRecursionCounter++; paralist *list = pj_expand_init(ctx, init); ctx->projStringParserCreateFromPROJStringRecursionCounter--; if (!list) { pj_dealloc(init); throw ParsingException("cannot expand " + projString); } std::string expanded; if (!d->title_.empty()) { expanded = "title=" + d->title_; } bool first = true; bool has_init_term = false; for (auto t = list; t;) { if (!expanded.empty()) { expanded += ' '; } if (first) { // first parameter is the init= itself first = false; } else if (starts_with(t->param, "init=")) { has_init_term = true; } else { expanded += t->param; } auto n = t->next; pj_dealloc(t); t = n; } for (const auto &pair : d->steps_[0].paramValues) { expanded += " +"; expanded += pair.key; if (!pair.value.empty()) { expanded += '='; expanded += pair.value; } } if (!has_init_term) { return createFromPROJString(expanded); } } int iFirstGeogStep = -1; int iSecondGeogStep = -1; int iProjStep = -1; int iFirstUnitConvert = -1; int iSecondUnitConvert = -1; int iFirstAxisSwap = -1; int iSecondAxisSwap = -1; bool unexpectedStructure = d->steps_.empty(); for (int i = 0; i < static_cast(d->steps_.size()); i++) { const auto &stepName = d->steps_[i].name; if (isGeographicStep(stepName)) { if (iFirstGeogStep < 0) { iFirstGeogStep = i; } else if (iSecondGeogStep < 0) { iSecondGeogStep = i; } else { unexpectedStructure = true; break; } } else if (ci_equal(stepName, "unitconvert")) { if (iFirstUnitConvert < 0) { iFirstUnitConvert = i; } else if (iSecondUnitConvert < 0) { iSecondUnitConvert = i; } else { unexpectedStructure = true; break; } } else if (ci_equal(stepName, "axisswap")) { if (iFirstAxisSwap < 0) { iFirstAxisSwap = i; } else if (iSecondAxisSwap < 0) { iSecondAxisSwap = i; } else { unexpectedStructure = true; break; } } else if (isProjectedStep(stepName)) { if (iProjStep >= 0) { unexpectedStructure = true; break; } iProjStep = i; } else { unexpectedStructure = true; break; } } if (!d->steps_.empty()) { // CRS candidate if ((d->steps_.size() == 1 && d->getParamValue(d->steps_[0], "type") != "crs") || (d->steps_.size() > 1 && d->getGlobalParamValue("type") != "crs")) { unexpectedStructure = true; } } struct Logger { std::string msg{}; // cppcheck-suppress functionStatic void setMessage(const char *msgIn) noexcept { try { msg = msgIn; } catch (const std::exception &) { } } static void log(void *user_data, int level, const char *msg) { if (level == PJ_LOG_ERROR) { static_cast(user_data)->setMessage(msg); } } }; // If the structure is not recognized, then try to instantiate the // pipeline, and if successful, wrap it in a PROJBasedOperation Logger logger; bool valid; auto pj_context = d->ctx_ ? d->ctx_ : proj_context_create(); if (!pj_context) { throw ParsingException("out of memory"); } if (pj_context != d->ctx_) { proj_log_func(pj_context, &logger, Logger::log); proj_context_use_proj4_init_rules(pj_context, d->usePROJ4InitRules_); } pj_context->projStringParserCreateFromPROJStringRecursionCounter++; auto log_level = proj_log_level(pj_context, PJ_LOG_NONE); auto pj = pj_create_internal( pj_context, (projString.find("type=crs") != std::string::npos ? projString + " +disable_grid_presence_check" : projString) .c_str()); pj_context->projStringParserCreateFromPROJStringRecursionCounter--; valid = pj != nullptr; proj_log_level(pj_context, log_level); // Remove parameters not understood by PROJ. if (valid && d->steps_.size() == 1) { std::vector newParamValues{}; std::set foundKeys; auto &step = d->steps_[0]; for (auto &kv : step.paramValues) { bool recognizedByPROJ = false; if (foundKeys.find(kv.key) != foundKeys.end()) { continue; } foundKeys.insert(kv.key); if (step.name == "krovak" && kv.key == "alpha") { // We recognize it in our CRS parsing code recognizedByPROJ = true; } else { for (auto cur = pj->params; cur; cur = cur->next) { const char *equal = strchr(cur->param, '='); if (equal && static_cast(equal - cur->param) == kv.key.size()) { if (memcmp(cur->param, kv.key.c_str(), kv.key.size()) == 0) { recognizedByPROJ = (cur->used == 1); break; } } else if (strcmp(cur->param, kv.key.c_str()) == 0) { recognizedByPROJ = (cur->used == 1); break; } } } if (recognizedByPROJ) { newParamValues.emplace_back(kv); } } step.paramValues = newParamValues; d->projString_.clear(); if (!step.name.empty()) { d->projString_ += step.isInit ? "+init=" : "+proj="; d->projString_ += step.name; } for (const auto ¶mValue : step.paramValues) { if (!d->projString_.empty()) { d->projString_ += ' '; } d->projString_ += '+'; d->projString_ += paramValue.key; if (!paramValue.value.empty()) { d->projString_ += '='; d->projString_ += pj_double_quote_string_param_if_needed(paramValue.value); } } } proj_destroy(pj); if (!valid) { std::string prefix("Error " + toString(proj_context_errno(pj_context)) + ": "); if (logger.msg.empty()) { logger.msg = prefix + proj_errno_string(proj_context_errno(pj_context)); } else { logger.msg = prefix + logger.msg; } } if (pj_context != d->ctx_) { proj_context_destroy(pj_context); } if (!valid) { throw ParsingException(logger.msg); } if (isGeocentricCRS) { // First run is dry run to mark all recognized/unrecognized tokens for (int iter = 0; iter < 2; iter++) { auto obj = d->buildBoundOrCompoundCRSIfNeeded( 0, d->buildGeocentricCRS(0, (d->steps_.size() == 2 && d->steps_[1].name == "unitconvert") ? 1 : -1)); if (iter == 1) { return nn_static_pointer_cast(obj); } } } if (!unexpectedStructure) { if (iFirstGeogStep == 0 && !d->steps_[iFirstGeogStep].inverted && iSecondGeogStep < 0 && iProjStep < 0 && (iFirstUnitConvert < 0 || iSecondUnitConvert < 0) && (iFirstAxisSwap < 0 || iSecondAxisSwap < 0)) { const bool ignoreVUnits = false; // First run is dry run to mark all recognized/unrecognized tokens for (int iter = 0; iter < 2; iter++) { auto obj = d->buildBoundOrCompoundCRSIfNeeded( 0, d->buildGeographicCRS(iFirstGeogStep, iFirstUnitConvert, iFirstAxisSwap, ignoreVUnits, false)); if (iter == 1) { return nn_static_pointer_cast(obj); } } } if (iProjStep >= 0 && !d->steps_[iProjStep].inverted && (iFirstGeogStep < 0 || iFirstGeogStep + 1 == iProjStep) && iSecondGeogStep < 0) { if (iFirstGeogStep < 0) iFirstGeogStep = iProjStep; const bool ignoreVUnits = true; // First run is dry run to mark all recognized/unrecognized tokens for (int iter = 0; iter < 2; iter++) { auto obj = d->buildBoundOrCompoundCRSIfNeeded( iProjStep, d->buildProjectedCRS( iProjStep, d->buildGeographicCRS( iFirstGeogStep, iFirstUnitConvert < iFirstGeogStep ? iFirstUnitConvert : -1, iFirstAxisSwap < iFirstGeogStep ? iFirstAxisSwap : -1, ignoreVUnits, true), iFirstUnitConvert < iFirstGeogStep ? iSecondUnitConvert : iFirstUnitConvert, iFirstAxisSwap < iFirstGeogStep ? iSecondAxisSwap : iFirstAxisSwap)); if (iter == 1) { return nn_static_pointer_cast(obj); } } } } auto props = PropertyMap(); if (!d->title_.empty()) { props.set(IdentifiedObject::NAME_KEY, d->title_); } return operation::SingleOperation::createPROJBased(props, projString, nullptr, nullptr, {}); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct JSONFormatter::Private { CPLJSonStreamingWriter writer_{nullptr, nullptr}; DatabaseContextPtr dbContext_{}; std::vector stackHasId_{false}; std::vector outputIdStack_{true}; bool allowIDInImmediateChild_ = false; bool omitTypeInImmediateChild_ = false; bool abridgedTransformation_ = false; std::string schema_ = PROJJSON_CURRENT_VERSION; std::string result_{}; // cppcheck-suppress functionStatic void pushOutputId(bool outputIdIn) { outputIdStack_.push_back(outputIdIn); } // cppcheck-suppress functionStatic void popOutputId() { outputIdStack_.pop_back(); } }; //! @endcond // --------------------------------------------------------------------------- /** \brief Constructs a new formatter. * * A formatter can be used only once (its internal state is mutated) * * @return new formatter. */ JSONFormatterNNPtr JSONFormatter::create( // cppcheck-suppress passedByValue DatabaseContextPtr dbContext) { auto ret = NN_NO_CHECK(JSONFormatter::make_unique()); ret->d->dbContext_ = dbContext; return ret; } // --------------------------------------------------------------------------- /** \brief Whether to use multi line output or not. */ JSONFormatter &JSONFormatter::setMultiLine(bool multiLine) noexcept { d->writer_.SetPrettyFormatting(multiLine); return *this; } // --------------------------------------------------------------------------- /** \brief Set number of spaces for each indentation level (defaults to 4). */ JSONFormatter &JSONFormatter::setIndentationWidth(int width) noexcept { d->writer_.SetIndentationSize(width); return *this; } // --------------------------------------------------------------------------- /** \brief Set the value of the "$schema" key in the top level object. * * If set to empty string, it will not be written. */ JSONFormatter &JSONFormatter::setSchema(const std::string &schema) noexcept { d->schema_ = schema; return *this; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress JSONFormatter::JSONFormatter() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- JSONFormatter::~JSONFormatter() = default; // --------------------------------------------------------------------------- CPLJSonStreamingWriter &JSONFormatter::writer() const { return d->writer_; } // --------------------------------------------------------------------------- bool JSONFormatter::outputId() const { return d->outputIdStack_.back(); } // --------------------------------------------------------------------------- bool JSONFormatter::outputUsage() const { return outputId() && d->outputIdStack_.size() == 2; } // --------------------------------------------------------------------------- void JSONFormatter::setAllowIDInImmediateChild() { d->allowIDInImmediateChild_ = true; } // --------------------------------------------------------------------------- void JSONFormatter::setOmitTypeInImmediateChild() { d->omitTypeInImmediateChild_ = true; } // --------------------------------------------------------------------------- JSONFormatter::ObjectContext::ObjectContext(JSONFormatter &formatter, const char *objectType, bool hasId) : m_formatter(formatter) { m_formatter.d->writer_.StartObj(); if (m_formatter.d->outputIdStack_.size() == 1 && !m_formatter.d->schema_.empty()) { m_formatter.d->writer_.AddObjKey("$schema"); m_formatter.d->writer_.Add(m_formatter.d->schema_); } if (objectType && !m_formatter.d->omitTypeInImmediateChild_) { m_formatter.d->writer_.AddObjKey("type"); m_formatter.d->writer_.Add(objectType); } m_formatter.d->omitTypeInImmediateChild_ = false; // All intermediate nodes shouldn't have ID if a parent has an ID // unless explicitly enabled. if (m_formatter.d->allowIDInImmediateChild_) { m_formatter.d->pushOutputId(m_formatter.d->outputIdStack_[0]); m_formatter.d->allowIDInImmediateChild_ = false; } else { m_formatter.d->pushOutputId(m_formatter.d->outputIdStack_[0] && !m_formatter.d->stackHasId_.back()); } m_formatter.d->stackHasId_.push_back(hasId || m_formatter.d->stackHasId_.back()); } // --------------------------------------------------------------------------- JSONFormatter::ObjectContext::~ObjectContext() { m_formatter.d->writer_.EndObj(); m_formatter.d->stackHasId_.pop_back(); m_formatter.d->popOutputId(); } // --------------------------------------------------------------------------- void JSONFormatter::setAbridgedTransformation(bool outputIn) { d->abridgedTransformation_ = outputIn; } // --------------------------------------------------------------------------- bool JSONFormatter::abridgedTransformation() const { return d->abridgedTransformation_; } //! @endcond // --------------------------------------------------------------------------- /** \brief Return the serialized JSON. */ const std::string &JSONFormatter::toString() const { return d->writer_.GetString(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress IJSONExportable::~IJSONExportable() = default; // --------------------------------------------------------------------------- std::string IJSONExportable::exportToJSON(JSONFormatter *formatter) const { _exportToJSON(formatter); return formatter->toString(); } //! @endcond } // namespace io NS_PROJ_END proj-6.3.1/src/iso19111/internal.cpp0000664000175000017500000002627413601752712013733 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/internal/internal.hpp" #include #include #ifdef _MSC_VER #include #else #include #endif #include #include // std::setprecision #include #include // std::istringstream and std::ostringstream #include #include "sqlite3.h" NS_PROJ_START namespace internal { // --------------------------------------------------------------------------- /** * Replace all occurrences of before with after. */ std::string replaceAll(const std::string &str, const std::string &before, const std::string &after) { std::string ret(str); const size_t nBeforeSize = before.size(); const size_t nAfterSize = after.size(); if (nBeforeSize) { size_t nStartPos = 0; while ((nStartPos = ret.find(before, nStartPos)) != std::string::npos) { ret.replace(nStartPos, nBeforeSize, after); nStartPos += nAfterSize; } } return ret; } // --------------------------------------------------------------------------- inline static bool EQUALN(const char *a, const char *b, size_t size) { #ifdef _MSC_VER return _strnicmp(a, b, size) == 0; #else return strncasecmp(a, b, size) == 0; #endif } /** * Case-insensitive equality test */ bool ci_equal(const std::string &a, const std::string &b) noexcept { const auto size = a.size(); if (size != b.size()) { return false; } return EQUALN(a.c_str(), b.c_str(), size); } bool ci_equal(const std::string &a, const char *b) noexcept { const auto size = a.size(); if (size != strlen(b)) { return false; } return EQUALN(a.c_str(), b, size); } bool ci_equal(const char *a, const char *b) noexcept { const auto size = strlen(a); if (size != strlen(b)) { return false; } return EQUALN(a, b, size); } // --------------------------------------------------------------------------- bool ci_less(const std::string &a, const std::string &b) noexcept { #ifdef _MSC_VER return _stricmp(a.c_str(), b.c_str()) < 0; #else return strcasecmp(a.c_str(), b.c_str()) < 0; #endif } // --------------------------------------------------------------------------- /** * Convert to lower case. */ std::string tolower(const std::string &str) { std::string ret(str); for (size_t i = 0; i < ret.size(); i++) ret[i] = static_cast(::tolower(ret[i])); return ret; } // --------------------------------------------------------------------------- /** * Convert to upper case. */ std::string toupper(const std::string &str) { std::string ret(str); for (size_t i = 0; i < ret.size(); i++) ret[i] = static_cast(::toupper(ret[i])); return ret; } // --------------------------------------------------------------------------- /** Strip leading and trailing double quote characters */ std::string stripQuotes(const std::string &str) { if (str.size() >= 2 && str[0] == '"' && str.back() == '"') { return str.substr(1, str.size() - 2); } return str; } // --------------------------------------------------------------------------- size_t ci_find(const std::string &str, const char *needle) noexcept { const size_t needleSize = strlen(needle); for (size_t i = 0; i + needleSize <= str.size(); i++) { if (EQUALN(str.c_str() + i, needle, needleSize)) { return i; } } return std::string::npos; } // --------------------------------------------------------------------------- size_t ci_find(const std::string &str, const std::string &needle, size_t startPos) noexcept { const size_t needleSize = needle.size(); for (size_t i = startPos; i + needleSize <= str.size(); i++) { if (EQUALN(str.c_str() + i, needle.c_str(), needleSize)) { return i; } } return std::string::npos; } // --------------------------------------------------------------------------- /* bool starts_with(const std::string &str, const std::string &prefix) noexcept { if (str.size() < prefix.size()) { return false; } return std::memcmp(str.c_str(), prefix.c_str(), prefix.size()) == 0; } */ // --------------------------------------------------------------------------- /* bool starts_with(const std::string &str, const char *prefix) noexcept { const size_t prefixSize = std::strlen(prefix); if (str.size() < prefixSize) { return false; } return std::memcmp(str.c_str(), prefix, prefixSize) == 0; } */ // --------------------------------------------------------------------------- bool ci_starts_with(const char *str, const char *prefix) noexcept { const auto str_size = strlen(str); const auto prefix_size = strlen(prefix); if (str_size < prefix_size) { return false; } return EQUALN(str, prefix, prefix_size); } // --------------------------------------------------------------------------- bool ci_starts_with(const std::string &str, const std::string &prefix) noexcept { if (str.size() < prefix.size()) { return false; } return EQUALN(str.c_str(), prefix.c_str(), prefix.size()); } // --------------------------------------------------------------------------- bool ends_with(const std::string &str, const std::string &suffix) noexcept { if (str.size() < suffix.size()) { return false; } return std::memcmp(str.c_str() + str.size() - suffix.size(), suffix.c_str(), suffix.size()) == 0; } // --------------------------------------------------------------------------- double c_locale_stod(const std::string &s) { const auto s_size = s.size(); // Fast path if (s_size > 0 && s_size < 15) { std::int64_t acc = 0; std::int64_t div = 1; bool afterDot = false; size_t i = 0; if (s[0] == '-') { ++i; div = -1; } else if (s[0] == '+') { ++i; } for (; i < s_size; ++i) { const auto ch = s[i]; if (ch >= '0' && ch <= '9') { acc = acc * 10 + ch - '0'; if (afterDot) { div *= 10; } } else if (ch == '.') { afterDot = true; } else { div = 0; } } if (div) { return static_cast(acc) / div; } } std::istringstream iss(s); iss.imbue(std::locale::classic()); double d; iss >> d; if (!iss.eof() || iss.fail()) { throw std::invalid_argument("non double value"); } return d; } // --------------------------------------------------------------------------- std::vector split(const std::string &str, char separator) { std::vector res; size_t lastPos = 0; size_t newPos = 0; while ((newPos = str.find(separator, lastPos)) != std::string::npos) { res.push_back(str.substr(lastPos, newPos - lastPos)); lastPos = newPos + 1; } res.push_back(str.substr(lastPos)); return res; } // --------------------------------------------------------------------------- std::vector split(const std::string &str, const std::string &separator) { std::vector res; size_t lastPos = 0; size_t newPos = 0; while ((newPos = str.find(separator, lastPos)) != std::string::npos) { res.push_back(str.substr(lastPos, newPos - lastPos)); lastPos = newPos + separator.size(); } res.push_back(str.substr(lastPos)); return res; } // --------------------------------------------------------------------------- #ifdef _WIN32 // For some reason, sqlite3_snprintf() in the sqlite3 builds used on AppVeyor // doesn't round identically to the Unix builds, and thus breaks a number of // unit test. So to avoid this, use the stdlib formatting std::string toString(int val) { std::ostringstream buffer; buffer.imbue(std::locale::classic()); buffer << val; return buffer.str(); } std::string toString(double val, int precision) { std::ostringstream buffer; buffer.imbue(std::locale::classic()); buffer << std::setprecision(precision); buffer << val; auto str = buffer.str(); if (precision == 15 && str.find("9999999999") != std::string::npos) { buffer.str(""); buffer.clear(); buffer << std::setprecision(14); buffer << val; return buffer.str(); } return str; } #else std::string toString(int val) { // use sqlite3 API that is slightly faster than std::ostringstream // with forcing the C locale. sqlite3_snprintf() emulates a C locale. constexpr int BUF_SIZE = 16; char szBuffer[BUF_SIZE]; sqlite3_snprintf(BUF_SIZE, szBuffer, "%d", val); return szBuffer; } std::string toString(double val, int precision) { // use sqlite3 API that is slightly faster than std::ostringstream // with forcing the C locale. sqlite3_snprintf() emulates a C locale. constexpr int BUF_SIZE = 32; char szBuffer[BUF_SIZE]; sqlite3_snprintf(BUF_SIZE, szBuffer, "%.*g", precision, val); if (precision == 15 && strstr(szBuffer, "9999999999")) { sqlite3_snprintf(BUF_SIZE, szBuffer, "%.14g", val); } return szBuffer; } #endif // --------------------------------------------------------------------------- std::string concat(const char *a, const std::string &b) { std::string res(a); res += b; return res; } std::string concat(const char *a, const std::string &b, const char *c) { std::string res(a); res += b; res += c; return res; } // --------------------------------------------------------------------------- } // namespace internal NS_PROJ_END proj-6.3.1/src/iso19111/datum.cpp0000664000175000017500000025003413617003642013220 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/datum.hpp" #include "proj/common.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_internal.h" #include "proj_api.h" // clang-format on #include #include #include #include using namespace NS_PROJ::internal; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace datum { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createMapNameEPSGCode(const char *name, int code) { return util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, code); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Datum::Private { util::optional anchorDefinition{}; util::optional publicationDate{}; common::IdentifiedObjectPtr conventionalRS{}; // cppcheck-suppress functionStatic void exportAnchorDefinition(io::WKTFormatter *formatter) const; // cppcheck-suppress functionStatic void exportAnchorDefinition(io::JSONFormatter *formatter) const; }; // --------------------------------------------------------------------------- void Datum::Private::exportAnchorDefinition(io::WKTFormatter *formatter) const { if (anchorDefinition) { formatter->startNode(io::WKTConstants::ANCHOR, false); formatter->addQuotedString(*anchorDefinition); formatter->endNode(); } } // --------------------------------------------------------------------------- void Datum::Private::exportAnchorDefinition( io::JSONFormatter *formatter) const { if (anchorDefinition) { auto &writer = formatter->writer(); writer.AddObjKey("anchor"); writer.Add(*anchorDefinition); } } //! @endcond // --------------------------------------------------------------------------- Datum::Datum() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- #ifdef notdef Datum::Datum(const Datum &other) : ObjectUsage(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Datum::~Datum() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the anchor definition. * * A description - possibly including coordinates of an identified point or * points - of the relationship used to anchor a coordinate system to the * Earth or alternate object. *
    *
  • For modern geodetic reference frames the anchor may be a set of station * coordinates; if the reference frame is dynamic it will also include * coordinate velocities. For a traditional geodetic datum, this anchor may be * a point known as the fundamental point, which is traditionally the point * where the relationship between geoid and ellipsoid is defined, together * with a direction from that point.
  • *
  • For a vertical reference frame the anchor may be the zero level at one * or more defined locations or a conventionally defined surface.
  • *
  • For an engineering datum, the anchor may be an identified physical point * with the orientation defined relative to the object.
  • *
* * @return the anchor definition, or empty. */ const util::optional &Datum::anchorDefinition() const { return d->anchorDefinition; } // --------------------------------------------------------------------------- /** \brief Return the date on which the datum definition was published. * * \note Departure from \ref ISO_19111_2019 : we return a DateTime instead of * a Citation::Date. * * @return the publication date, or empty. */ const util::optional &Datum::publicationDate() const { return d->publicationDate; } // --------------------------------------------------------------------------- /** \brief Return the conventional reference system. * * This is the name, identifier, alias and remarks for the terrestrial * reference system or vertical reference system realized by this reference * frame, for example "ITRS" for ITRF88 through ITRF2008 and ITRF2014, or * "EVRS" for EVRF2000 and EVRF2007. * * @return the conventional reference system, or nullptr. */ const common::IdentifiedObjectPtr &Datum::conventionalRS() const { return d->conventionalRS; } // --------------------------------------------------------------------------- void Datum::setAnchor(const util::optional &anchor) { d->anchorDefinition = anchor; } // --------------------------------------------------------------------------- void Datum::setProperties( const util::PropertyMap &properties) // throw(InvalidValueTypeException) { std::string publicationDate; properties.getStringValue("PUBLICATION_DATE", publicationDate); if (!publicationDate.empty()) { d->publicationDate = common::DateTime::create(publicationDate); } ObjectUsage::setProperties(properties); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool Datum::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDatum = dynamic_cast(other); if (otherDatum == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { if ((anchorDefinition().has_value() ^ otherDatum->anchorDefinition().has_value())) { return false; } if (anchorDefinition().has_value() && otherDatum->anchorDefinition().has_value() && *anchorDefinition() != *otherDatum->anchorDefinition()) { return false; } if ((publicationDate().has_value() ^ otherDatum->publicationDate().has_value())) { return false; } if (publicationDate().has_value() && otherDatum->publicationDate().has_value() && publicationDate()->toString() != otherDatum->publicationDate()->toString()) { return false; } if (((conventionalRS() != nullptr) ^ (otherDatum->conventionalRS() != nullptr))) { return false; } if (conventionalRS() && otherDatum->conventionalRS() && conventionalRS()->_isEquivalentTo( otherDatum->conventionalRS().get(), criterion, dbContext)) { return false; } } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct PrimeMeridian::Private { common::Angle longitude_{}; explicit Private(const common::Angle &longitude) : longitude_(longitude) {} }; //! @endcond // --------------------------------------------------------------------------- PrimeMeridian::PrimeMeridian(const common::Angle &longitudeIn) : d(internal::make_unique(longitudeIn)) {} // --------------------------------------------------------------------------- #ifdef notdef PrimeMeridian::PrimeMeridian(const PrimeMeridian &other) : common::IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PrimeMeridian::~PrimeMeridian() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the longitude of the prime meridian. * * It is measured from the internationally-recognised reference meridian * ('Greenwich meridian'), positive eastward. * The default value is 0 degrees. * * @return the longitude of the prime meridian. */ const common::Angle &PrimeMeridian::longitude() PROJ_PURE_DEFN { return d->longitude_; } // --------------------------------------------------------------------------- /** \brief Instantiate a PrimeMeridian. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param longitudeIn the longitude of the prime meridian. * @return new PrimeMeridian. */ PrimeMeridianNNPtr PrimeMeridian::create(const util::PropertyMap &properties, const common::Angle &longitudeIn) { auto pm(PrimeMeridian::nn_make_shared(longitudeIn)); pm->setProperties(properties); return pm; } // --------------------------------------------------------------------------- const PrimeMeridianNNPtr PrimeMeridian::createGREENWICH() { return create(createMapNameEPSGCode("Greenwich", 8901), common::Angle(0)); } // --------------------------------------------------------------------------- const PrimeMeridianNNPtr PrimeMeridian::createREFERENCE_MERIDIAN() { return create(util::PropertyMap().set(IdentifiedObject::NAME_KEY, "Reference meridian"), common::Angle(0)); } // --------------------------------------------------------------------------- const PrimeMeridianNNPtr PrimeMeridian::createPARIS() { return create(createMapNameEPSGCode("Paris", 8903), common::Angle(2.5969213, common::UnitOfMeasure::GRAD)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void PrimeMeridian::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; std::string l_name = name()->description().has_value() ? nameStr() : "Greenwich"; if (!(isWKT2 && formatter->primeMeridianOmittedIfGreenwich() && l_name == "Greenwich")) { formatter->startNode(io::WKTConstants::PRIMEM, !identifiers().empty()); formatter->addQuotedString(l_name); const auto &l_long = longitude(); if (formatter->primeMeridianInDegree()) { formatter->add(l_long.convertToUnit(common::UnitOfMeasure::DEGREE)); } else { formatter->add(l_long.value()); } const auto &unit = l_long.unit(); if (isWKT2) { if (!(formatter ->primeMeridianOrParameterUnitOmittedIfSameAsAxis() && unit == *(formatter->axisAngularUnit()))) { unit._exportToWKT(formatter, io::WKTConstants::ANGLEUNIT); } } else if (!formatter->primeMeridianInDegree()) { unit._exportToWKT(formatter); } if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void PrimeMeridian::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("PrimeMeridian", !identifiers().empty())); writer.AddObjKey("name"); std::string l_name = name()->description().has_value() ? nameStr() : "Greenwich"; writer.Add(l_name); const auto &l_long = longitude(); writer.AddObjKey("longitude"); const auto &unit = l_long.unit(); if (unit == common::UnitOfMeasure::DEGREE) { writer.Add(l_long.value(), 15); } else { auto longitudeContext(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("value"); writer.Add(l_long.value(), 15); writer.AddObjKey("unit"); unit._exportToJSON(formatter); } if (formatter->outputId()) { formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::string PrimeMeridian::getPROJStringWellKnownName(const common::Angle &angle) { const double valRad = angle.getSIValue(); std::string projPMName; projCtx ctxt = pj_ctx_alloc(); auto proj_pm = proj_list_prime_meridians(); for (int i = 0; proj_pm[i].id != nullptr; ++i) { double valRefRad = dmstor_ctx(ctxt, proj_pm[i].defn, nullptr); if (::fabs(valRad - valRefRad) < 1e-10) { projPMName = proj_pm[i].id; break; } } pj_ctx_free(ctxt); return projPMName; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void PrimeMeridian::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { if (longitude().getSIValue() != 0) { std::string projPMName(getPROJStringWellKnownName(longitude())); if (!projPMName.empty()) { formatter->addParam("pm", projPMName); } else { const double valDeg = longitude().convertToUnit(common::UnitOfMeasure::DEGREE); formatter->addParam("pm", valDeg); } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool PrimeMeridian::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherPM = dynamic_cast(other); if (otherPM == nullptr || !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // In MapInfo, the Paris prime meridian is returned as 2.3372291666667 // instead of the official value of 2.33722917, which is a relative // error in the 1e-9 range. return longitude()._isEquivalentTo(otherPM->longitude(), criterion, 1e-8); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Ellipsoid::Private { common::Length semiMajorAxis_{}; util::optional inverseFlattening_{}; util::optional semiMinorAxis_{}; util::optional semiMedianAxis_{}; std::string celestialBody_{}; explicit Private(const common::Length &radius, const std::string &celestialBody) : semiMajorAxis_(radius), celestialBody_(celestialBody) {} Private(const common::Length &semiMajorAxisIn, const common::Scale &invFlattening, const std::string &celestialBody) : semiMajorAxis_(semiMajorAxisIn), inverseFlattening_(invFlattening), celestialBody_(celestialBody) {} Private(const common::Length &semiMajorAxisIn, const common::Length &semiMinorAxisIn, const std::string &celestialBody) : semiMajorAxis_(semiMajorAxisIn), semiMinorAxis_(semiMinorAxisIn), celestialBody_(celestialBody) {} }; //! @endcond // --------------------------------------------------------------------------- Ellipsoid::Ellipsoid(const common::Length &radius, const std::string &celestialBodyIn) : d(internal::make_unique(radius, celestialBodyIn)) {} // --------------------------------------------------------------------------- Ellipsoid::Ellipsoid(const common::Length &semiMajorAxisIn, const common::Scale &invFlattening, const std::string &celestialBodyIn) : d(internal::make_unique(semiMajorAxisIn, invFlattening, celestialBodyIn)) {} // --------------------------------------------------------------------------- Ellipsoid::Ellipsoid(const common::Length &semiMajorAxisIn, const common::Length &semiMinorAxisIn, const std::string &celestialBodyIn) : d(internal::make_unique(semiMajorAxisIn, semiMinorAxisIn, celestialBodyIn)) {} // --------------------------------------------------------------------------- #ifdef notdef Ellipsoid::Ellipsoid(const Ellipsoid &other) : common::IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Ellipsoid::~Ellipsoid() = default; Ellipsoid::Ellipsoid(const Ellipsoid &other) : IdentifiedObject(other), d(internal::make_unique(*(other.d))) {} //! @endcond // --------------------------------------------------------------------------- /** \brief Return the length of the semi-major axis of the ellipsoid. * * @return the semi-major axis. */ const common::Length &Ellipsoid::semiMajorAxis() PROJ_PURE_DEFN { return d->semiMajorAxis_; } // --------------------------------------------------------------------------- /** \brief Return the inverse flattening value of the ellipsoid, if the * ellipsoid * has been defined with this value. * * @see computeInverseFlattening() that will always return a valid value of the * inverse flattening, whether the ellipsoid has been defined through inverse * flattening or semi-minor axis. * * @return the inverse flattening value of the ellipsoid, or empty. */ const util::optional & Ellipsoid::inverseFlattening() PROJ_PURE_DEFN { return d->inverseFlattening_; } // --------------------------------------------------------------------------- /** \brief Return the length of the semi-minor axis of the ellipsoid, if the * ellipsoid * has been defined with this value. * * @see computeSemiMinorAxis() that will always return a valid value of the * semi-minor axis, whether the ellipsoid has been defined through inverse * flattening or semi-minor axis. * * @return the semi-minor axis of the ellipsoid, or empty. */ const util::optional & Ellipsoid::semiMinorAxis() PROJ_PURE_DEFN { return d->semiMinorAxis_; } // --------------------------------------------------------------------------- /** \brief Return whether the ellipsoid is spherical. * * That is to say is semiMajorAxis() == computeSemiMinorAxis(). * * A sphere is completely defined by the semi-major axis, which is the radius * of the sphere. * * @return true if the ellipsoid is spherical. */ bool Ellipsoid::isSphere() PROJ_PURE_DEFN { if (d->inverseFlattening_.has_value()) { return d->inverseFlattening_->value() == 0; } if (semiMinorAxis().has_value()) { return semiMajorAxis() == *semiMinorAxis(); } return true; } // --------------------------------------------------------------------------- /** \brief Return the length of the semi-median axis of a triaxial ellipsoid * * This parameter is not required for a biaxial ellipsoid. * * @return the semi-median axis of the ellipsoid, or empty. */ const util::optional & Ellipsoid::semiMedianAxis() PROJ_PURE_DEFN { return d->semiMedianAxis_; } // --------------------------------------------------------------------------- /** \brief Return or compute the inverse flattening value of the ellipsoid. * * If computed, the inverse flattening is the result of a / (a - b), * where a is the semi-major axis and b the semi-minor axis. * * @return the inverse flattening value of the ellipsoid, or 0 for a sphere. */ double Ellipsoid::computedInverseFlattening() PROJ_PURE_DEFN { if (d->inverseFlattening_.has_value()) { return d->inverseFlattening_->getSIValue(); } if (d->semiMinorAxis_.has_value()) { const double a = d->semiMajorAxis_.getSIValue(); const double b = d->semiMinorAxis_->getSIValue(); return (a == b) ? 0.0 : a / (a - b); } return 0.0; } // --------------------------------------------------------------------------- /** \brief Return the squared eccentricity of the ellipsoid. * * @return the squared eccentricity, or a negative value if invalid. */ double Ellipsoid::squaredEccentricity() PROJ_PURE_DEFN { const double rf = computedInverseFlattening(); const double f = rf != 0.0 ? 1. / rf : 0.0; const double e2 = f * (2 - f); return e2; } // --------------------------------------------------------------------------- /** \brief Return or compute the length of the semi-minor axis of the ellipsoid. * * If computed, the semi-minor axis is the result of a * (1 - 1 / rf) * where a is the semi-major axis and rf the reverse/inverse flattening. * @return the semi-minor axis of the ellipsoid. */ common::Length Ellipsoid::computeSemiMinorAxis() const { if (d->semiMinorAxis_.has_value()) { return *d->semiMinorAxis_; } if (inverseFlattening().has_value()) { return common::Length( (1.0 - 1.0 / d->inverseFlattening_->getSIValue()) * d->semiMajorAxis_.value(), d->semiMajorAxis_.unit()); } return d->semiMajorAxis_; } // --------------------------------------------------------------------------- /** \brief Return the name of the celestial body on which the ellipsoid refers * to. */ const std::string &Ellipsoid::celestialBody() PROJ_PURE_DEFN { return d->celestialBody_; } // --------------------------------------------------------------------------- /** \brief Instantiate a Ellipsoid as a sphere. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param radius the sphere radius (semi-major axis). * @param celestialBody Name of the celestial body on which the ellipsoid refers * to. * @return new Ellipsoid. */ EllipsoidNNPtr Ellipsoid::createSphere(const util::PropertyMap &properties, const common::Length &radius, const std::string &celestialBody) { auto ellipsoid(Ellipsoid::nn_make_shared(radius, celestialBody)); ellipsoid->setProperties(properties); return ellipsoid; } // --------------------------------------------------------------------------- /** \brief Instantiate a Ellipsoid from its inverse/reverse flattening. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param semiMajorAxisIn the semi-major axis. * @param invFlattening the inverse/reverse flattening. If set to 0, this will * be considered as a sphere. * @param celestialBody Name of the celestial body on which the ellipsoid refers * to. * @return new Ellipsoid. */ EllipsoidNNPtr Ellipsoid::createFlattenedSphere( const util::PropertyMap &properties, const common::Length &semiMajorAxisIn, const common::Scale &invFlattening, const std::string &celestialBody) { auto ellipsoid(invFlattening.value() == 0 ? Ellipsoid::nn_make_shared(semiMajorAxisIn, celestialBody) : Ellipsoid::nn_make_shared( semiMajorAxisIn, invFlattening, celestialBody)); ellipsoid->setProperties(properties); return ellipsoid; } // --------------------------------------------------------------------------- /** \brief Instantiate a Ellipsoid from the value of its two semi axis. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param semiMajorAxisIn the semi-major axis. * @param semiMinorAxisIn the semi-minor axis. * @param celestialBody Name of the celestial body on which the ellipsoid refers * to. * @return new Ellipsoid. */ EllipsoidNNPtr Ellipsoid::createTwoAxis(const util::PropertyMap &properties, const common::Length &semiMajorAxisIn, const common::Length &semiMinorAxisIn, const std::string &celestialBody) { auto ellipsoid(Ellipsoid::nn_make_shared( semiMajorAxisIn, semiMinorAxisIn, celestialBody)); ellipsoid->setProperties(properties); return ellipsoid; } // --------------------------------------------------------------------------- const EllipsoidNNPtr Ellipsoid::createCLARKE_1866() { return createTwoAxis(createMapNameEPSGCode("Clarke 1866", 7008), common::Length(6378206.4), common::Length(6356583.8)); } // --------------------------------------------------------------------------- const EllipsoidNNPtr Ellipsoid::createWGS84() { return createFlattenedSphere(createMapNameEPSGCode("WGS 84", 7030), common::Length(6378137), common::Scale(298.257223563)); } // --------------------------------------------------------------------------- const EllipsoidNNPtr Ellipsoid::createGRS1980() { return createFlattenedSphere(createMapNameEPSGCode("GRS 1980", 7019), common::Length(6378137), common::Scale(298.257222101)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Ellipsoid::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::ELLIPSOID : io::WKTConstants::SPHEROID, !identifiers().empty()); { auto l_name = nameStr(); if (l_name.empty()) { formatter->addQuotedString("unnamed"); } else { if (formatter->useESRIDialect()) { if (l_name == "WGS 84") { l_name = "WGS_1984"; } else { bool aliasFound = false; const auto &dbContext = formatter->databaseContext(); if (dbContext) { auto l_alias = dbContext->getAliasFromOfficialName( l_name, "ellipsoid", "ESRI"); if (!l_alias.empty()) { l_name = l_alias; aliasFound = true; } } if (!aliasFound) { l_name = io::WKTFormatter::morphNameToESRI(l_name); } } } formatter->addQuotedString(l_name); } const auto &semiMajor = semiMajorAxis(); if (isWKT2) { formatter->add(semiMajor.value()); } else { formatter->add(semiMajor.getSIValue()); } formatter->add(computedInverseFlattening()); const auto &unit = semiMajor.unit(); if (isWKT2 && !(formatter->ellipsoidUnitOmittedIfMetre() && unit == common::UnitOfMeasure::METRE)) { unit._exportToWKT(formatter, io::WKTConstants::LENGTHUNIT); } if (formatter->outputId()) { formatID(formatter); } } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Ellipsoid::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("Ellipsoid", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } const auto &semiMajor = semiMajorAxis(); const auto &semiMajorUnit = semiMajor.unit(); writer.AddObjKey(isSphere() ? "radius" : "semi_major_axis"); if (semiMajorUnit == common::UnitOfMeasure::METRE) { writer.Add(semiMajor.value(), 15); } else { auto objContext(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("value"); writer.Add(semiMajor.value(), 15); writer.AddObjKey("unit"); semiMajorUnit._exportToJSON(formatter); } if (!isSphere()) { const auto &l_inverseFlattening = inverseFlattening(); if (l_inverseFlattening.has_value()) { writer.AddObjKey("inverse_flattening"); writer.Add(l_inverseFlattening->getSIValue(), 15); } else { writer.AddObjKey("semi_minor_axis"); const auto &l_semiMinorAxis(semiMinorAxis()); const auto &semiMinorAxisUnit(l_semiMinorAxis->unit()); if (semiMinorAxisUnit == common::UnitOfMeasure::METRE) { writer.Add(l_semiMinorAxis->value(), 15); } else { auto objContext(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("value"); writer.Add(l_semiMinorAxis->value(), 15); writer.AddObjKey("unit"); semiMinorAxisUnit._exportToJSON(formatter); } } } if (formatter->outputId()) { formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- bool Ellipsoid::lookForProjWellKnownEllps(std::string &projEllpsName, std::string &ellpsName) const { const double a = semiMajorAxis().getSIValue(); const double b = computeSemiMinorAxis().getSIValue(); const double rf = computedInverseFlattening(); auto proj_ellps = proj_list_ellps(); for (int i = 0; proj_ellps[i].id != nullptr; i++) { assert(strncmp(proj_ellps[i].major, "a=", 2) == 0); const double a_iter = c_locale_stod(proj_ellps[i].major + 2); if (::fabs(a - a_iter) < 1e-10 * a_iter) { if (strncmp(proj_ellps[i].ell, "b=", 2) == 0) { const double b_iter = c_locale_stod(proj_ellps[i].ell + 2); if (::fabs(b - b_iter) < 1e-10 * b_iter) { projEllpsName = proj_ellps[i].id; ellpsName = proj_ellps[i].name; if (starts_with(ellpsName, "GRS 1980")) { ellpsName = "GRS 1980"; } return true; } } else { assert(strncmp(proj_ellps[i].ell, "rf=", 3) == 0); const double rf_iter = c_locale_stod(proj_ellps[i].ell + 3); if (::fabs(rf - rf_iter) < 1e-10 * rf_iter) { projEllpsName = proj_ellps[i].id; ellpsName = proj_ellps[i].name; if (starts_with(ellpsName, "GRS 1980")) { ellpsName = "GRS 1980"; } return true; } } } } return false; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Ellipsoid::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { const double a = semiMajorAxis().getSIValue(); std::string projEllpsName; std::string ellpsName; if (lookForProjWellKnownEllps(projEllpsName, ellpsName)) { formatter->addParam("ellps", projEllpsName); return; } if (isSphere()) { formatter->addParam("R", a); } else { formatter->addParam("a", a); if (inverseFlattening().has_value()) { const double rf = computedInverseFlattening(); formatter->addParam("rf", rf); } else { const double b = computeSemiMinorAxis().getSIValue(); formatter->addParam("b", b); } } } //! @endcond // --------------------------------------------------------------------------- /** \brief Return a Ellipsoid object where some parameters are better * identified. * * @return a new Ellipsoid. */ EllipsoidNNPtr Ellipsoid::identify() const { auto newEllipsoid = Ellipsoid::nn_make_shared(*this); newEllipsoid->assignSelf( util::nn_static_pointer_cast(newEllipsoid)); if (name()->description()->empty() || nameStr() == "unknown") { std::string projEllpsName; std::string ellpsName; if (lookForProjWellKnownEllps(projEllpsName, ellpsName)) { newEllipsoid->setProperties( util::PropertyMap().set(IdentifiedObject::NAME_KEY, ellpsName)); } } return newEllipsoid; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool Ellipsoid::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherEllipsoid = dynamic_cast(other); if (otherEllipsoid == nullptr || (criterion == util::IComparable::Criterion::STRICT && !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext))) { return false; } // PROJ "clrk80" name is "Clarke 1880 mod." and GDAL tends to // export to it a number of Clarke 1880 variants, so be lax if (criterion != util::IComparable::Criterion::STRICT && (nameStr() == "Clarke 1880 mod." || otherEllipsoid->nameStr() == "Clarke 1880 mod.")) { return std::fabs(semiMajorAxis().getSIValue() - otherEllipsoid->semiMajorAxis().getSIValue()) < 1e-8 * semiMajorAxis().getSIValue() && std::fabs(computedInverseFlattening() - otherEllipsoid->computedInverseFlattening()) < 1e-5 * computedInverseFlattening(); } if (!semiMajorAxis()._isEquivalentTo(otherEllipsoid->semiMajorAxis(), criterion)) { return false; } const auto &l_semiMinorAxis = semiMinorAxis(); const auto &l_other_semiMinorAxis = otherEllipsoid->semiMinorAxis(); if (l_semiMinorAxis.has_value() && l_other_semiMinorAxis.has_value()) { if (!l_semiMinorAxis->_isEquivalentTo(*l_other_semiMinorAxis, criterion)) { return false; } } const auto &l_inverseFlattening = inverseFlattening(); const auto &l_other_sinverseFlattening = otherEllipsoid->inverseFlattening(); if (l_inverseFlattening.has_value() && l_other_sinverseFlattening.has_value()) { if (!l_inverseFlattening->_isEquivalentTo(*l_other_sinverseFlattening, criterion)) { return false; } } if (criterion == util::IComparable::Criterion::STRICT) { if ((l_semiMinorAxis.has_value() ^ l_other_semiMinorAxis.has_value())) { return false; } if ((l_inverseFlattening.has_value() ^ l_other_sinverseFlattening.has_value())) { return false; } } else { if (!otherEllipsoid->computeSemiMinorAxis()._isEquivalentTo( otherEllipsoid->computeSemiMinorAxis(), criterion)) { return false; } } const auto &l_semiMedianAxis = semiMedianAxis(); const auto &l_other_semiMedianAxis = otherEllipsoid->semiMedianAxis(); if ((l_semiMedianAxis.has_value() ^ l_other_semiMedianAxis.has_value())) { return false; } if (l_semiMedianAxis.has_value() && l_other_semiMedianAxis.has_value()) { if (!l_semiMedianAxis->_isEquivalentTo(*l_other_semiMedianAxis, criterion)) { return false; } } return true; } //! @endcond // --------------------------------------------------------------------------- std::string Ellipsoid::guessBodyName(const io::DatabaseContextPtr &dbContext, double a) { constexpr double relError = 0.005; constexpr double earthMeanRadius = 6375000.0; if (std::fabs(a - earthMeanRadius) < relError * earthMeanRadius) { return Ellipsoid::EARTH; } if (dbContext) { try { auto factory = io::AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string()); return factory->identifyBodyFromSemiMajorAxis(a, relError); } catch (const std::exception &) { } } return "Non-Earth body"; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeodeticReferenceFrame::Private { PrimeMeridianNNPtr primeMeridian_; EllipsoidNNPtr ellipsoid_; Private(const EllipsoidNNPtr &ellipsoidIn, const PrimeMeridianNNPtr &primeMeridianIn) : primeMeridian_(primeMeridianIn), ellipsoid_(ellipsoidIn) {} }; //! @endcond // --------------------------------------------------------------------------- GeodeticReferenceFrame::GeodeticReferenceFrame( const EllipsoidNNPtr &ellipsoidIn, const PrimeMeridianNNPtr &primeMeridianIn) : d(internal::make_unique(ellipsoidIn, primeMeridianIn)) {} // --------------------------------------------------------------------------- #ifdef notdef GeodeticReferenceFrame::GeodeticReferenceFrame( const GeodeticReferenceFrame &other) : Datum(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeodeticReferenceFrame::~GeodeticReferenceFrame() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the PrimeMeridian associated with a GeodeticReferenceFrame. * * @return the PrimeMeridian. */ const PrimeMeridianNNPtr & GeodeticReferenceFrame::primeMeridian() PROJ_PURE_DEFN { return d->primeMeridian_; } // --------------------------------------------------------------------------- /** \brief Return the Ellipsoid associated with a GeodeticReferenceFrame. * * \note The \ref ISO_19111_2019 modelling allows (but discourages) a * GeodeticReferenceFrame * to not be associated with a Ellipsoid in the case where it is used by a * geocentric crs::GeodeticCRS. We have made the choice of making the ellipsoid * specification compulsory. * * @return the Ellipsoid. */ const EllipsoidNNPtr &GeodeticReferenceFrame::ellipsoid() PROJ_PURE_DEFN { return d->ellipsoid_; } // --------------------------------------------------------------------------- /** \brief Instantiate a GeodeticReferenceFrame * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param ellipsoid the Ellipsoid. * @param anchor the anchor definition, or empty. * @param primeMeridian the PrimeMeridian. * @return new GeodeticReferenceFrame. */ GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::create(const util::PropertyMap &properties, const EllipsoidNNPtr &ellipsoid, const util::optional &anchor, const PrimeMeridianNNPtr &primeMeridian) { GeodeticReferenceFrameNNPtr grf( GeodeticReferenceFrame::nn_make_shared( ellipsoid, primeMeridian)); grf->setAnchor(anchor); grf->setProperties(properties); return grf; } // --------------------------------------------------------------------------- const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::createEPSG_6267() { return create(createMapNameEPSGCode("North American Datum 1927", 6267), Ellipsoid::CLARKE_1866, util::optional(), PrimeMeridian::GREENWICH); } // --------------------------------------------------------------------------- const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::createEPSG_6269() { return create(createMapNameEPSGCode("North American Datum 1983", 6269), Ellipsoid::GRS1980, util::optional(), PrimeMeridian::GREENWICH); } // --------------------------------------------------------------------------- const GeodeticReferenceFrameNNPtr GeodeticReferenceFrame::createEPSG_6326() { return create(createMapNameEPSGCode("World Geodetic System 1984", 6326), Ellipsoid::WGS84, util::optional(), PrimeMeridian::GREENWICH); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticReferenceFrame::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const auto &ids = identifiers(); formatter->startNode(io::WKTConstants::DATUM, !ids.empty()); auto l_name = nameStr(); if (l_name.empty()) { l_name = "unnamed"; } if (!isWKT2) { if (formatter->useESRIDialect()) { if (l_name == "World Geodetic System 1984") { l_name = "D_WGS_1984"; } else { bool aliasFound = false; const auto &dbContext = formatter->databaseContext(); if (dbContext) { auto l_alias = dbContext->getAliasFromOfficialName( l_name, "geodetic_datum", "ESRI"); size_t pos; if (!l_alias.empty()) { l_name = l_alias; aliasFound = true; } else if ((pos = l_name.find(" (")) != std::string::npos) { l_alias = dbContext->getAliasFromOfficialName( l_name.substr(0, pos), "geodetic_datum", "ESRI"); if (!l_alias.empty()) { l_name = l_alias; aliasFound = true; } } } if (!aliasFound) { l_name = io::WKTFormatter::morphNameToESRI(l_name); if (!starts_with(l_name, "D_")) { l_name = "D_" + l_name; } } } } else { // Replace spaces by underscore for datum names coming from EPSG // so as to emulate GDAL < 3 importFromEPSG() if (ids.size() == 1 && *(ids.front()->codeSpace()) == "EPSG") { l_name = io::WKTFormatter::morphNameToESRI(l_name); } else if (ids.empty()) { const auto &dbContext = formatter->databaseContext(); if (dbContext) { auto factory = io::AuthorityFactory::create( NN_NO_CHECK(dbContext), std::string()); // We use anonymous autority and approximate matching, so // as to trigger the caching done in createObjectsFromName() // in that case. auto matches = factory->createObjectsFromName( l_name, {io::AuthorityFactory::ObjectType:: GEODETIC_REFERENCE_FRAME}, true, 2); if (matches.size() == 1) { const auto &match = matches.front(); const auto &matchId = match->identifiers(); if (matchId.size() == 1 && *(matchId.front()->codeSpace()) == "EPSG" && metadata::Identifier::isEquivalentName( l_name.c_str(), match->nameStr().c_str())) { l_name = io::WKTFormatter::morphNameToESRI(l_name); } } } } if (l_name == "World_Geodetic_System_1984") { l_name = "WGS_1984"; } } } formatter->addQuotedString(l_name); ellipsoid()->_exportToWKT(formatter); if (isWKT2) { Datum::getPrivate()->exportAnchorDefinition(formatter); } else { const auto &TOWGS84Params = formatter->getTOWGS84Parameters(); if (TOWGS84Params.size() == 7) { formatter->startNode(io::WKTConstants::TOWGS84, false); for (const auto &val : TOWGS84Params) { formatter->add(val, 12); } formatter->endNode(); } std::string extension = formatter->getHDatumExtension(); if (!extension.empty()) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4_GRIDS"); formatter->addQuotedString(extension); formatter->endNode(); } } if (formatter->outputId()) { formatID(formatter); } // the PRIMEM is exported as a child of the CRS formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void GeodeticReferenceFrame::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto dynamicGRF = dynamic_cast(this); auto objectContext(formatter->MakeObjectContext( dynamicGRF ? "DynamicGeodeticReferenceFrame" : "GeodeticReferenceFrame", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } Datum::getPrivate()->exportAnchorDefinition(formatter); if (dynamicGRF) { writer.AddObjKey("frame_reference_epoch"); writer.Add(dynamicGRF->frameReferenceEpoch().value()); const auto &deformationModel = dynamicGRF->deformationModelName(); if (deformationModel.has_value()) { writer.AddObjKey("deformation_model"); writer.Add(*deformationModel); } } writer.AddObjKey("ellipsoid"); formatter->setOmitTypeInImmediateChild(); ellipsoid()->_exportToJSON(formatter); const auto &l_primeMeridian(primeMeridian()); if (l_primeMeridian->nameStr() != "Greenwich") { writer.AddObjKey("prime_meridian"); formatter->setOmitTypeInImmediateChild(); primeMeridian()->_exportToJSON(formatter); } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool GeodeticReferenceFrame::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherGRF = dynamic_cast(other); if (otherGRF == nullptr || !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return primeMeridian()->_isEquivalentTo(otherGRF->primeMeridian().get(), criterion, dbContext) && ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- bool GeodeticReferenceFrame::hasEquivalentNameToUsingAlias( const IdentifiedObject *other, const io::DatabaseContextPtr &dbContext) const { if (dbContext) { if (!identifiers().empty()) { const auto &id = identifiers().front(); auto aliases = dbContext->getAliases(*(id->codeSpace()), id->code(), nameStr(), "geodetic_datum", std::string()); const char *otherName = other->nameStr().c_str(); for (const auto &alias : aliases) { if (metadata::Identifier::isEquivalentName(otherName, alias.c_str())) { return true; } } return false; } else if (!other->identifiers().empty()) { auto otherGRF = dynamic_cast(other); if (otherGRF) { return otherGRF->hasEquivalentNameToUsingAlias(this, dbContext); } return false; } auto aliases = dbContext->getAliases(std::string(), std::string(), nameStr(), "geodetic_datum", std::string()); const char *otherName = other->nameStr().c_str(); for (const auto &alias : aliases) { if (metadata::Identifier::isEquivalentName(otherName, alias.c_str())) { return true; } } } return false; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DynamicGeodeticReferenceFrame::Private { common::Measure frameReferenceEpoch{}; util::optional deformationModelName{}; explicit Private(const common::Measure &frameReferenceEpochIn) : frameReferenceEpoch(frameReferenceEpochIn) {} }; //! @endcond // --------------------------------------------------------------------------- DynamicGeodeticReferenceFrame::DynamicGeodeticReferenceFrame( const EllipsoidNNPtr &ellipsoidIn, const PrimeMeridianNNPtr &primeMeridianIn, const common::Measure &frameReferenceEpochIn, const util::optional &deformationModelNameIn) : GeodeticReferenceFrame(ellipsoidIn, primeMeridianIn), d(internal::make_unique(frameReferenceEpochIn)) { d->deformationModelName = deformationModelNameIn; } // --------------------------------------------------------------------------- #ifdef notdef DynamicGeodeticReferenceFrame::DynamicGeodeticReferenceFrame( const DynamicGeodeticReferenceFrame &other) : GeodeticReferenceFrame(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DynamicGeodeticReferenceFrame::~DynamicGeodeticReferenceFrame() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the epoch to which the coordinates of stations defining the * dynamic geodetic reference frame are referenced. * * Usually given as a decimal year e.g. 2016.47. * * @return the frame reference epoch. */ const common::Measure & DynamicGeodeticReferenceFrame::frameReferenceEpoch() const { return d->frameReferenceEpoch; } // --------------------------------------------------------------------------- /** \brief Return the name of the deformation model. * * @note This is an extension to the \ref ISO_19111_2019 modeling, to * hold the content of the DYNAMIC.MODEL WKT2 node. * * @return the name of the deformation model. */ const util::optional & DynamicGeodeticReferenceFrame::deformationModelName() const { return d->deformationModelName; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool DynamicGeodeticReferenceFrame::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast(other); if (otherDGRF == nullptr || !GeodeticReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( otherDGRF->frameReferenceEpoch(), criterion) && metadata::Identifier::isEquivalentName( deformationModelName()->c_str(), otherDGRF->deformationModelName()->c_str()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DynamicGeodeticReferenceFrame::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (isWKT2 && formatter->use2019Keywords()) { formatter->startNode(io::WKTConstants::DYNAMIC, false); formatter->startNode(io::WKTConstants::FRAMEEPOCH, false); formatter->add( frameReferenceEpoch().convertToUnit(common::UnitOfMeasure::YEAR)); formatter->endNode(); if (deformationModelName().has_value() && !deformationModelName()->empty()) { formatter->startNode(io::WKTConstants::MODEL, false); formatter->addQuotedString(*deformationModelName()); formatter->endNode(); } formatter->endNode(); } GeodeticReferenceFrame::_exportToWKT(formatter); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a DynamicGeodeticReferenceFrame * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param ellipsoid the Ellipsoid. * @param anchor the anchor definition, or empty. * @param primeMeridian the PrimeMeridian. * @param frameReferenceEpochIn the frame reference epoch. * @param deformationModelNameIn deformation model name, or empty * @return new DynamicGeodeticReferenceFrame. */ DynamicGeodeticReferenceFrameNNPtr DynamicGeodeticReferenceFrame::create( const util::PropertyMap &properties, const EllipsoidNNPtr &ellipsoid, const util::optional &anchor, const PrimeMeridianNNPtr &primeMeridian, const common::Measure &frameReferenceEpochIn, const util::optional &deformationModelNameIn) { DynamicGeodeticReferenceFrameNNPtr grf( DynamicGeodeticReferenceFrame::nn_make_shared< DynamicGeodeticReferenceFrame>(ellipsoid, primeMeridian, frameReferenceEpochIn, deformationModelNameIn)); grf->setAnchor(anchor); grf->setProperties(properties); return grf; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DatumEnsemble::Private { std::vector datums{}; metadata::PositionalAccuracyNNPtr positionalAccuracy; Private(const std::vector &datumsIn, const metadata::PositionalAccuracyNNPtr &accuracy) : datums(datumsIn), positionalAccuracy(accuracy) {} }; //! @endcond // --------------------------------------------------------------------------- DatumEnsemble::DatumEnsemble(const std::vector &datumsIn, const metadata::PositionalAccuracyNNPtr &accuracy) : d(internal::make_unique(datumsIn, accuracy)) {} // --------------------------------------------------------------------------- #ifdef notdef DatumEnsemble::DatumEnsemble(const DatumEnsemble &other) : common::IdentifiedObject(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DatumEnsemble::~DatumEnsemble() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the set of datums which may be considered to be * insignificantly different from each other. * * @return the set of datums of the DatumEnsemble. */ const std::vector &DatumEnsemble::datums() const { return d->datums; } // --------------------------------------------------------------------------- /** \brief Return the inaccuracy introduced through use of this collection of * datums. * * It is an indication of the differences in coordinate values at all points * between the various realizations that have been grouped into this datum * ensemble. * * @return the accuracy. */ const metadata::PositionalAccuracyNNPtr & DatumEnsemble::positionalAccuracy() const { return d->positionalAccuracy; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DatumEnsemble::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2 || !formatter->use2019Keywords()) { throw io::FormattingException( "DatumEnsemble can only be exported to WKT2:2019"); } auto l_datums = datums(); assert(!l_datums.empty()); formatter->startNode(io::WKTConstants::ENSEMBLE, false); const auto &l_name = nameStr(); if (!l_name.empty()) { formatter->addQuotedString(l_name); } else { formatter->addQuotedString("unnamed"); } for (const auto &datum : l_datums) { formatter->startNode(io::WKTConstants::MEMBER, !datum->identifiers().empty()); const auto &l_datum_name = datum->nameStr(); if (!l_datum_name.empty()) { formatter->addQuotedString(l_datum_name); } else { formatter->addQuotedString("unnamed"); } if (formatter->outputId()) { datum->formatID(formatter); } formatter->endNode(); } auto grfFirst = std::dynamic_pointer_cast( l_datums[0].as_nullable()); if (grfFirst) { grfFirst->ellipsoid()->_exportToWKT(formatter); } formatter->startNode(io::WKTConstants::ENSEMBLEACCURACY, false); formatter->add(positionalAccuracy()->value()); formatter->endNode(); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DatumEnsemble::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto objectContext( formatter->MakeObjectContext("DatumEnsemble", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } auto l_datums = datums(); writer.AddObjKey("members"); { auto membersContext(writer.MakeArrayContext(false)); for (const auto &datum : l_datums) { auto memberContext(writer.MakeObjectContext()); writer.AddObjKey("name"); const auto &l_datum_name = datum->nameStr(); if (!l_datum_name.empty()) { writer.Add(l_datum_name); } else { writer.Add("unnamed"); } datum->formatID(formatter); } } auto grfFirst = std::dynamic_pointer_cast( l_datums[0].as_nullable()); if (grfFirst) { writer.AddObjKey("ellipsoid"); formatter->setOmitTypeInImmediateChild(); grfFirst->ellipsoid()->_exportToJSON(formatter); } writer.AddObjKey("accuracy"); writer.Add(positionalAccuracy()->value()); formatID(formatter); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a DatumEnsemble. * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param datumsIn Array of at least 2 datums. * @param accuracy Accuracy of the datum ensemble * @return new DatumEnsemble. * @throw util::Exception */ DatumEnsembleNNPtr DatumEnsemble::create( const util::PropertyMap &properties, const std::vector &datumsIn, const metadata::PositionalAccuracyNNPtr &accuracy) // throw(Exception) { if (datumsIn.size() < 2) { throw util::Exception("ensemble should have at least 2 datums"); } if (auto grfFirst = dynamic_cast(datumsIn[0].get())) { for (size_t i = 1; i < datumsIn.size(); i++) { auto grf = dynamic_cast(datumsIn[i].get()); if (!grf) { throw util::Exception( "ensemble should have consistent datum types"); } if (!grfFirst->ellipsoid()->_isEquivalentTo( grf->ellipsoid().get())) { throw util::Exception( "ensemble should have datums with identical ellipsoid"); } if (!grfFirst->primeMeridian()->_isEquivalentTo( grf->primeMeridian().get())) { throw util::Exception( "ensemble should have datums with identical " "prime meridian"); } } } else if (dynamic_cast(datumsIn[0].get())) { for (size_t i = 1; i < datumsIn.size(); i++) { if (!dynamic_cast(datumsIn[i].get())) { throw util::Exception( "ensemble should have consistent datum types"); } } } auto ensemble( DatumEnsemble::nn_make_shared(datumsIn, accuracy)); ensemble->setProperties(properties); return ensemble; } // --------------------------------------------------------------------------- RealizationMethod::RealizationMethod(const std::string &nameIn) : CodeList(nameIn) {} // --------------------------------------------------------------------------- RealizationMethod::RealizationMethod(const RealizationMethod &) = default; // --------------------------------------------------------------------------- RealizationMethod &RealizationMethod:: operator=(const RealizationMethod &other) { CodeList::operator=(other); return *this; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct VerticalReferenceFrame::Private { util::optional realizationMethod_{}; }; //! @endcond // --------------------------------------------------------------------------- VerticalReferenceFrame::VerticalReferenceFrame( const util::optional &realizationMethodIn) : d(internal::make_unique()) { if (!realizationMethodIn->toString().empty()) { d->realizationMethod_ = *realizationMethodIn; } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress VerticalReferenceFrame::~VerticalReferenceFrame() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the method through which this vertical reference frame is * realized. * * @return the realization method. */ const util::optional & VerticalReferenceFrame::realizationMethod() const { return d->realizationMethod_; } // --------------------------------------------------------------------------- /** \brief Instantiate a VerticalReferenceFrame * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param anchor the anchor definition, or empty. * @param realizationMethodIn the realization method, or empty. * @return new VerticalReferenceFrame. */ VerticalReferenceFrameNNPtr VerticalReferenceFrame::create( const util::PropertyMap &properties, const util::optional &anchor, const util::optional &realizationMethodIn) { auto rf(VerticalReferenceFrame::nn_make_shared( realizationMethodIn)); rf->setAnchor(anchor); rf->setProperties(properties); return rf; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalReferenceFrame::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::VDATUM : io::WKTConstants::VERT_DATUM, !identifiers().empty()); const auto &l_name = nameStr(); if (!l_name.empty()) { formatter->addQuotedString(l_name); } else { formatter->addQuotedString("unnamed"); } if (isWKT2) { Datum::getPrivate()->exportAnchorDefinition(formatter); } else { formatter->add(2005); // CS_VD_GeoidModelDerived from OGC 01-009 const auto &extension = formatter->getVDatumExtension(); if (!extension.empty()) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4_GRIDS"); formatter->addQuotedString(extension); formatter->endNode(); } } if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void VerticalReferenceFrame::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto dynamicGRF = dynamic_cast(this); auto objectContext(formatter->MakeObjectContext( dynamicGRF ? "DynamicVerticalReferenceFrame" : "VerticalReferenceFrame", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } Datum::getPrivate()->exportAnchorDefinition(formatter); if (dynamicGRF) { writer.AddObjKey("frame_reference_epoch"); writer.Add(dynamicGRF->frameReferenceEpoch().value()); const auto &deformationModel = dynamicGRF->deformationModelName(); if (deformationModel.has_value()) { writer.AddObjKey("deformation_model"); writer.Add(*deformationModel); } } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool VerticalReferenceFrame::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherVRF = dynamic_cast(other); if (otherVRF == nullptr || !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } if ((realizationMethod().has_value() ^ otherVRF->realizationMethod().has_value())) { return false; } if (realizationMethod().has_value() && otherVRF->realizationMethod().has_value()) { if (*(realizationMethod()) != *(otherVRF->realizationMethod())) { return false; } } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DynamicVerticalReferenceFrame::Private { common::Measure frameReferenceEpoch{}; util::optional deformationModelName{}; explicit Private(const common::Measure &frameReferenceEpochIn) : frameReferenceEpoch(frameReferenceEpochIn) {} }; //! @endcond // --------------------------------------------------------------------------- DynamicVerticalReferenceFrame::DynamicVerticalReferenceFrame( const util::optional &realizationMethodIn, const common::Measure &frameReferenceEpochIn, const util::optional &deformationModelNameIn) : VerticalReferenceFrame(realizationMethodIn), d(internal::make_unique(frameReferenceEpochIn)) { d->deformationModelName = deformationModelNameIn; } // --------------------------------------------------------------------------- #ifdef notdef DynamicVerticalReferenceFrame::DynamicVerticalReferenceFrame( const DynamicVerticalReferenceFrame &other) : VerticalReferenceFrame(other), d(internal::make_unique(*other.d)) {} #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DynamicVerticalReferenceFrame::~DynamicVerticalReferenceFrame() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the epoch to which the coordinates of stations defining the * dynamic geodetic reference frame are referenced. * * Usually given as a decimal year e.g. 2016.47. * * @return the frame reference epoch. */ const common::Measure & DynamicVerticalReferenceFrame::frameReferenceEpoch() const { return d->frameReferenceEpoch; } // --------------------------------------------------------------------------- /** \brief Return the name of the deformation model. * * @note This is an extension to the \ref ISO_19111_2019 modeling, to * hold the content of the DYNAMIC.MODEL WKT2 node. * * @return the name of the deformation model. */ const util::optional & DynamicVerticalReferenceFrame::deformationModelName() const { return d->deformationModelName; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool DynamicVerticalReferenceFrame::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDGRF = dynamic_cast(other); if (otherDGRF == nullptr || !VerticalReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) { return false; } return frameReferenceEpoch()._isEquivalentTo( otherDGRF->frameReferenceEpoch(), criterion) && metadata::Identifier::isEquivalentName( deformationModelName()->c_str(), otherDGRF->deformationModelName()->c_str()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void DynamicVerticalReferenceFrame::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (isWKT2 && formatter->use2019Keywords()) { formatter->startNode(io::WKTConstants::DYNAMIC, false); formatter->startNode(io::WKTConstants::FRAMEEPOCH, false); formatter->add( frameReferenceEpoch().convertToUnit(common::UnitOfMeasure::YEAR)); formatter->endNode(); if (!deformationModelName()->empty()) { formatter->startNode(io::WKTConstants::MODEL, false); formatter->addQuotedString(*deformationModelName()); formatter->endNode(); } formatter->endNode(); } VerticalReferenceFrame::_exportToWKT(formatter); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a DynamicVerticalReferenceFrame * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param anchor the anchor definition, or empty. * @param realizationMethodIn the realization method, or empty. * @param frameReferenceEpochIn the frame reference epoch. * @param deformationModelNameIn deformation model name, or empty * @return new DynamicVerticalReferenceFrame. */ DynamicVerticalReferenceFrameNNPtr DynamicVerticalReferenceFrame::create( const util::PropertyMap &properties, const util::optional &anchor, const util::optional &realizationMethodIn, const common::Measure &frameReferenceEpochIn, const util::optional &deformationModelNameIn) { DynamicVerticalReferenceFrameNNPtr grf( DynamicVerticalReferenceFrame::nn_make_shared< DynamicVerticalReferenceFrame>(realizationMethodIn, frameReferenceEpochIn, deformationModelNameIn)); grf->setAnchor(anchor); grf->setProperties(properties); return grf; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct TemporalDatum::Private { common::DateTime temporalOrigin_; std::string calendar_; Private(const common::DateTime &temporalOriginIn, const std::string &calendarIn) : temporalOrigin_(temporalOriginIn), calendar_(calendarIn) {} }; //! @endcond // --------------------------------------------------------------------------- TemporalDatum::TemporalDatum(const common::DateTime &temporalOriginIn, const std::string &calendarIn) : d(internal::make_unique(temporalOriginIn, calendarIn)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TemporalDatum::~TemporalDatum() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the date and time to which temporal coordinates are * referenced, expressed in conformance with ISO 8601. * * @return the temporal origin. */ const common::DateTime &TemporalDatum::temporalOrigin() const { return d->temporalOrigin_; } // --------------------------------------------------------------------------- /** \brief Return the calendar to which the temporal origin is referenced * * Default value: TemporalDatum::CALENDAR_PROLEPTIC_GREGORIAN. * * @return the calendar. */ const std::string &TemporalDatum::calendar() const { return d->calendar_; } // --------------------------------------------------------------------------- /** \brief Instantiate a TemporalDatum * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param temporalOriginIn the temporal origin into which temporal coordinates * are referenced. * @param calendarIn the calendar (generally * TemporalDatum::CALENDAR_PROLEPTIC_GREGORIAN) * @return new TemporalDatum. */ TemporalDatumNNPtr TemporalDatum::create(const util::PropertyMap &properties, const common::DateTime &temporalOriginIn, const std::string &calendarIn) { auto datum(TemporalDatum::nn_make_shared(temporalOriginIn, calendarIn)); datum->setProperties(properties); return datum; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void TemporalDatum::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { throw io::FormattingException( "TemporalDatum can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::TDATUM, !identifiers().empty()); formatter->addQuotedString(nameStr()); if (formatter->use2019Keywords()) { formatter->startNode(io::WKTConstants::CALENDAR, false); formatter->addQuotedString(calendar()); formatter->endNode(); } const auto &timeOriginStr = temporalOrigin().toString(); if (!timeOriginStr.empty()) { formatter->startNode(io::WKTConstants::TIMEORIGIN, false); if (temporalOrigin().isISO_8601()) { formatter->add(timeOriginStr); } else { formatter->addQuotedString(timeOriginStr); } formatter->endNode(); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void TemporalDatum::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto objectContext( formatter->MakeObjectContext("TemporalDatum", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); writer.Add(nameStr()); writer.AddObjKey("calendar"); writer.Add(calendar()); const auto &timeOriginStr = temporalOrigin().toString(); if (!timeOriginStr.empty()) { writer.AddObjKey("time_origin"); writer.Add(timeOriginStr); } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool TemporalDatum::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return temporalOrigin().toString() == otherTD->temporalOrigin().toString() && calendar() == otherTD->calendar(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct EngineeringDatum::Private {}; //! @endcond // --------------------------------------------------------------------------- EngineeringDatum::EngineeringDatum() : d(nullptr) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress EngineeringDatum::~EngineeringDatum() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a EngineeringDatum * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param anchor the anchor definition, or empty. * @return new EngineeringDatum. */ EngineeringDatumNNPtr EngineeringDatum::create(const util::PropertyMap &properties, const util::optional &anchor) { auto datum(EngineeringDatum::nn_make_shared()); datum->setAnchor(anchor); datum->setProperties(properties); return datum; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void EngineeringDatum::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::EDATUM : io::WKTConstants::LOCAL_DATUM, !identifiers().empty()); formatter->addQuotedString(nameStr()); if (isWKT2) { Datum::getPrivate()->exportAnchorDefinition(formatter); } else { // Somewhat picked up arbitrarily from OGC 01-009: // CS_LD_Max (Attribute) : 32767 // Highest possible value for local datum types. formatter->add(32767); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void EngineeringDatum::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto objectContext(formatter->MakeObjectContext("EngineeringDatum", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); writer.Add(nameStr()); Datum::getPrivate()->exportAnchorDefinition(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool EngineeringDatum::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ParametricDatum::Private {}; //! @endcond // --------------------------------------------------------------------------- ParametricDatum::ParametricDatum() : d(nullptr) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ParametricDatum::~ParametricDatum() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a ParametricDatum * * @param properties See \ref general_properties. * At minimum the name should be defined. * @param anchor the anchor definition, or empty. * @return new ParametricDatum. */ ParametricDatumNNPtr ParametricDatum::create(const util::PropertyMap &properties, const util::optional &anchor) { auto datum(ParametricDatum::nn_make_shared()); datum->setAnchor(anchor); datum->setProperties(properties); return datum; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ParametricDatum::_exportToWKT( io::WKTFormatter *formatter) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { throw io::FormattingException( "ParametricDatum can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::PDATUM, !identifiers().empty()); formatter->addQuotedString(nameStr()); Datum::getPrivate()->exportAnchorDefinition(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ParametricDatum::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto objectContext(formatter->MakeObjectContext("ParametricDatum", !identifiers().empty())); auto &writer = formatter->writer(); writer.AddObjKey("name"); writer.Add(nameStr()); Datum::getPrivate()->exportAnchorDefinition(formatter); ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool ParametricDatum::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherTD = dynamic_cast(other); if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion, dbContext)) { return false; } return true; } //! @endcond } // namespace datum NS_PROJ_END proj-6.3.1/src/iso19111/util.cpp0000664000175000017500000005514013601752712013066 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/util.hpp" #include "proj/io.hpp" #include "proj/internal/internal.hpp" #include #include #include using namespace NS_PROJ::internal; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace util { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct BaseObject::Private { // This is a manual implementation of std::enable_shared_from_this<> that // avoids publicly deriving from it. std::weak_ptr self_{}; }; //! @endcond // --------------------------------------------------------------------------- BaseObject::BaseObject() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress BaseObject::~BaseObject() = default; // --------------------------------------------------------------------------- BaseObjectNNPtr::~BaseObjectNNPtr() = default; //! @endcond // --------------------------------------------------------------------------- /** Keep a reference to ourselves as an internal weak pointer. So that * extractGeographicBaseObject() can later return a shared pointer on itself. */ void BaseObject::assignSelf(const BaseObjectNNPtr &self) { assert(self.get() == this); d->self_ = self.as_nullable(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress BaseObjectNNPtr BaseObject::shared_from_this() const { // This assertion checks that in all code paths where we create a // shared pointer, we took care of assigning it to self_, by calling // assignSelf(); return NN_CHECK_ASSERT(d->self_.lock()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct BoxedValue::Private { BoxedValue::Type type_{BoxedValue::Type::INTEGER}; std::string stringValue_{}; int integerValue_{}; bool booleanValue_{}; explicit Private(const std::string &stringValueIn) : type_(BoxedValue::Type::STRING), stringValue_(stringValueIn) {} explicit Private(int integerValueIn) : type_(BoxedValue::Type::INTEGER), integerValue_(integerValueIn) {} explicit Private(bool booleanValueIn) : type_(BoxedValue::Type::BOOLEAN), booleanValue_(booleanValueIn) {} }; //! @endcond // --------------------------------------------------------------------------- BoxedValue::BoxedValue() : d(internal::make_unique(std::string())) {} // --------------------------------------------------------------------------- /** \brief Constructs a BoxedValue from a string. */ BoxedValue::BoxedValue(const char *stringValueIn) : d(internal::make_unique( std::string(stringValueIn ? stringValueIn : ""))) {} // --------------------------------------------------------------------------- /** \brief Constructs a BoxedValue from a string. */ BoxedValue::BoxedValue(const std::string &stringValueIn) : d(internal::make_unique(stringValueIn)) {} // --------------------------------------------------------------------------- /** \brief Constructs a BoxedValue from an integer. */ BoxedValue::BoxedValue(int integerValueIn) : d(internal::make_unique(integerValueIn)) {} // --------------------------------------------------------------------------- /** \brief Constructs a BoxedValue from a boolean. */ BoxedValue::BoxedValue(bool booleanValueIn) : d(internal::make_unique(booleanValueIn)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress BoxedValue::BoxedValue(const BoxedValue &other) : d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- BoxedValue::~BoxedValue() = default; // --------------------------------------------------------------------------- const BoxedValue::Type &BoxedValue::type() const { return d->type_; } // --------------------------------------------------------------------------- const std::string &BoxedValue::stringValue() const { return d->stringValue_; } // --------------------------------------------------------------------------- int BoxedValue::integerValue() const { return d->integerValue_; } // --------------------------------------------------------------------------- bool BoxedValue::booleanValue() const { return d->booleanValue_; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ArrayOfBaseObject::Private { std::vector values_{}; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ArrayOfBaseObject::ArrayOfBaseObject() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- ArrayOfBaseObject::~ArrayOfBaseObject() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Adds an object to the array. * * @param obj the object to add. */ void ArrayOfBaseObject::add(const BaseObjectNNPtr &obj) { d->values_.emplace_back(obj); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::vector::const_iterator ArrayOfBaseObject::begin() const { return d->values_.begin(); } // --------------------------------------------------------------------------- std::vector::const_iterator ArrayOfBaseObject::end() const { return d->values_.end(); } // --------------------------------------------------------------------------- bool ArrayOfBaseObject::empty() const { return d->values_.empty(); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a ArrayOfBaseObject. * * @return a new ArrayOfBaseObject. */ ArrayOfBaseObjectNNPtr ArrayOfBaseObject::create() { return ArrayOfBaseObject::nn_make_shared(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct PropertyMap::Private { std::list> list_{}; // cppcheck-suppress functionStatic void set(const std::string &key, const BoxedValueNNPtr &val) { for (auto &pair : list_) { if (pair.first == key) { pair.second = val; return; } } list_.emplace_back(key, val); } }; //! @endcond // --------------------------------------------------------------------------- PropertyMap::PropertyMap() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PropertyMap::PropertyMap(const PropertyMap &other) : d(internal::make_unique(*(other.d))) {} //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PropertyMap::~PropertyMap() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const BaseObjectNNPtr *PropertyMap::get(const std::string &key) const { for (const auto &pair : d->list_) { if (pair.first == key) { return &(pair.second); } } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void PropertyMap::unset(const std::string &key) { for (auto iter = d->list_.begin(); iter != d->list_.end(); ++iter) { if (iter->first == key) { d->list_.erase(iter); return; } } } //! @endcond // --------------------------------------------------------------------------- /** \brief Set a BaseObjectNNPtr as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const BaseObjectNNPtr &val) { for (auto &pair : d->list_) { if (pair.first == key) { pair.second = val; return *this; } } d->list_.emplace_back(key, val); return *this; } // --------------------------------------------------------------------------- /** \brief Set a string as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const std::string &val) { d->set(key, util::nn_make_shared(val)); return *this; } // --------------------------------------------------------------------------- /** \brief Set a string as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const char *val) { d->set(key, util::nn_make_shared(val)); return *this; } // --------------------------------------------------------------------------- /** \brief Set a integer as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, int val) { d->set(key, util::nn_make_shared(val)); return *this; } // --------------------------------------------------------------------------- /** \brief Set a boolean as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, bool val) { d->set(key, util::nn_make_shared(val)); return *this; } // --------------------------------------------------------------------------- /** \brief Set a vector of strings as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const std::vector &arrayIn) { ArrayOfBaseObjectNNPtr array = ArrayOfBaseObject::create(); for (const auto &str : arrayIn) { array->add(util::nn_make_shared(str)); } return set(key, array); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool PropertyMap::getStringValue( const std::string &key, std::string &outVal) const // throw(InvalidValueTypeException) { for (const auto &pair : d->list_) { if (pair.first == key) { auto genVal = dynamic_cast(pair.second.get()); if (genVal && genVal->type() == BoxedValue::Type::STRING) { outVal = genVal->stringValue(); return true; } throw InvalidValueTypeException("Invalid value type for " + key); } } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool PropertyMap::getStringValue( const std::string &key, optional &outVal) const // throw(InvalidValueTypeException) { for (const auto &pair : d->list_) { if (pair.first == key) { auto genVal = dynamic_cast(pair.second.get()); if (genVal && genVal->type() == BoxedValue::Type::STRING) { outVal = genVal->stringValue(); return true; } throw InvalidValueTypeException("Invalid value type for " + key); } } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GenericName::Private {}; //! @endcond // --------------------------------------------------------------------------- GenericName::GenericName() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- GenericName::GenericName(const GenericName &other) : d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GenericName::~GenericName() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct NameSpace::Private { GenericNamePtr name{}; bool isGlobal{}; std::string separator = std::string(":"); std::string separatorHead = std::string(":"); }; //! @endcond // --------------------------------------------------------------------------- NameSpace::NameSpace(const GenericNamePtr &nameIn) : d(internal::make_unique()) { d->name = nameIn; } // --------------------------------------------------------------------------- NameSpace::NameSpace(const NameSpace &other) : d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress NameSpace::~NameSpace() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether this is a global namespace. */ bool NameSpace::isGlobal() const { return d->isGlobal; } // --------------------------------------------------------------------------- NameSpaceNNPtr NameSpace::getGlobalFromThis() const { NameSpaceNNPtr ns(NameSpace::nn_make_shared(*this)); ns->d->isGlobal = true; ns->d->name = LocalName::make_shared("global"); return ns; } // --------------------------------------------------------------------------- /** \brief Returns the name of this namespace. */ const GenericNamePtr &NameSpace::name() const { return d->name; } // --------------------------------------------------------------------------- const std::string &NameSpace::separator() const { return d->separator; } // --------------------------------------------------------------------------- NameSpaceNNPtr NameSpace::createGLOBAL() { NameSpaceNNPtr ns(NameSpace::nn_make_shared( LocalName::make_shared("global"))); ns->d->isGlobal = true; return ns; } // --------------------------------------------------------------------------- const NameSpaceNNPtr NameSpace::GLOBAL(NameSpace::createGLOBAL()); // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct LocalName::Private { NameSpacePtr scope{}; std::string name{}; }; //! @endcond // --------------------------------------------------------------------------- LocalName::LocalName(const std::string &name) : d(internal::make_unique()) { d->name = name; } // --------------------------------------------------------------------------- LocalName::LocalName(const NameSpacePtr &ns, const std::string &name) : d(internal::make_unique()) { d->scope = ns ? ns : static_cast(NameSpace::GLOBAL); d->name = name; } // --------------------------------------------------------------------------- LocalName::LocalName(const LocalName &other) : GenericName(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress LocalName::~LocalName() = default; //! @endcond // --------------------------------------------------------------------------- const NameSpacePtr LocalName::scope() const { if (d->scope) return d->scope; return NameSpace::GLOBAL; } // --------------------------------------------------------------------------- GenericNameNNPtr LocalName::toFullyQualifiedName() const { if (scope()->isGlobal()) return LocalName::nn_make_shared(*this); return LocalName::nn_make_shared( d->scope->getGlobalFromThis(), d->scope->name()->toFullyQualifiedName()->toString() + d->scope->separator() + d->name); } // --------------------------------------------------------------------------- std::string LocalName::toString() const { return d->name; } // --------------------------------------------------------------------------- /** \brief Instantiate a NameSpace. * * @param name name of the namespace. * @param properties Properties. Allowed keys are "separator" and * "separator.head". * @return a new NameFactory. */ NameSpaceNNPtr NameFactory::createNameSpace(const GenericNameNNPtr &name, const PropertyMap &properties) { NameSpaceNNPtr ns(NameSpace::nn_make_shared(name)); properties.getStringValue("separator", ns->d->separator); properties.getStringValue("separator.head", ns->d->separatorHead); return ns; } // --------------------------------------------------------------------------- /** \brief Instantiate a LocalName. * * @param scope scope. * @param name string of the local name. * @return a new LocalName. */ LocalNameNNPtr NameFactory::createLocalName(const NameSpacePtr &scope, const std::string &name) { return LocalName::nn_make_shared(scope, name); } // --------------------------------------------------------------------------- /** \brief Instantiate a GenericName. * * @param scope scope. * @param parsedNames the components of the name. * @return a new GenericName. */ GenericNameNNPtr NameFactory::createGenericName(const NameSpacePtr &scope, const std::vector &parsedNames) { std::string name; const std::string separator(scope ? scope->separator() : NameSpace::GLOBAL->separator()); bool first = true; for (const auto &str : parsedNames) { if (!first) name += separator; first = false; name += str; } return LocalName::nn_make_shared(scope, name); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CodeList::~CodeList() = default; //! @endcond // --------------------------------------------------------------------------- CodeList &CodeList::operator=(const CodeList &other) { name_ = other.name_; return *this; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Exception::Exception(const char *message) : msg_(message) {} // --------------------------------------------------------------------------- Exception::Exception(const std::string &message) : msg_(message) {} // --------------------------------------------------------------------------- Exception::Exception(const Exception &) = default; // --------------------------------------------------------------------------- Exception::~Exception() = default; //! @endcond // --------------------------------------------------------------------------- /** Return the exception text. */ const char *Exception::what() const noexcept { return msg_.c_str(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress InvalidValueTypeException::InvalidValueTypeException(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- InvalidValueTypeException::InvalidValueTypeException(const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- InvalidValueTypeException::~InvalidValueTypeException() = default; // --------------------------------------------------------------------------- InvalidValueTypeException::InvalidValueTypeException( const InvalidValueTypeException &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress UnsupportedOperationException::UnsupportedOperationException( const char *message) : Exception(message) {} // --------------------------------------------------------------------------- UnsupportedOperationException::UnsupportedOperationException( const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- UnsupportedOperationException::~UnsupportedOperationException() = default; // --------------------------------------------------------------------------- UnsupportedOperationException::UnsupportedOperationException( const UnsupportedOperationException &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress IComparable::~IComparable() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Returns whether an object is equivalent to another one. * @param other other object to compare to * @param criterion comparaison criterion. * @param dbContext Database context, or nullptr. * @return true if objects are equivalent. */ bool IComparable::isEquivalentTo( const IComparable *other, Criterion criterion, const io::DatabaseContextPtr &dbContext) const { return _isEquivalentTo(other, criterion, dbContext); } // --------------------------------------------------------------------------- } // namespace util NS_PROJ_END proj-6.3.1/src/iso19111/coordinateoperation.cpp0000664000175000017500000230614213617003642016162 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #define FROM_COORDINATE_OPERATION_CPP #include "proj/coordinateoperation.hpp" #include "proj/common.hpp" #include "proj/crs.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj/internal/tracing.hpp" // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_internal.h" // M_PI // clang-format on #include #include #include #include #include #include #include #include // #define TRACE_CREATE_OPERATIONS // #define DEBUG_SORT // #define DEBUG_CONCATENATED_OPERATION #if defined(DEBUG_SORT) || defined(DEBUG_CONCATENATED_OPERATION) #include #endif using namespace NS_PROJ::internal; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn > >::~nn() = default; template<> nn > >::~nn() = default; }} #endif #include "proj/internal/coordinateoperation_constants.hpp" #include "proj/internal/coordinateoperation_internal.hpp" #include "proj/internal/esri_projection_mappings.hpp" #if 0 namespace dropbox{ namespace oxygen { template<> nn>::~nn() = default; template<> nn>::~nn() = default; template<> nn>::~nn() = default; template<> nn::~nn() = default; }} #endif // --------------------------------------------------------------------------- NS_PROJ_START namespace operation { //! @cond Doxygen_Suppress constexpr double UTM_LATITUDE_OF_NATURAL_ORIGIN = 0.0; constexpr double UTM_SCALE_FACTOR = 0.9996; constexpr double UTM_FALSE_EASTING = 500000.0; constexpr double UTM_NORTH_FALSE_NORTHING = 0.0; constexpr double UTM_SOUTH_FALSE_NORTHING = 10000000.0; static const std::string INVERSE_OF = "Inverse of "; static const char *BALLPARK_GEOCENTRIC_TRANSLATION = "Ballpark geocentric translation"; static const char *NULL_GEOGRAPHIC_OFFSET = "Null geographic offset"; static const char *NULL_GEOCENTRIC_TRANSLATION = "Null geocentric translation"; static const char *BALLPARK_GEOGRAPHIC_OFFSET = "Ballpark geographic offset"; static const char *BALLPARK_VERTICAL_TRANSFORMATION_PREFIX = " (ballpark vertical transformation"; static const char *BALLPARK_VERTICAL_TRANSFORMATION = " (ballpark vertical transformation)"; static const char *BALLPARK_VERTICAL_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT = " (ballpark vertical transformation, without ellipsoid height to vertical " "height correction)"; static const std::string AXIS_ORDER_CHANGE_2D_NAME = "axis order change (2D)"; //! @endcond //! @cond Doxygen_Suppress static util::PropertyMap createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom, bool approximateInversion); //! @endcond // --------------------------------------------------------------------------- #ifdef TRACE_CREATE_OPERATIONS //! @cond Doxygen_Suppress static std::string objectAsStr(const common::IdentifiedObject *obj) { std::string ret(obj->nameStr()); const auto &ids = obj->identifiers(); if (!ids.empty()) { ret += " ("; ret += (*ids[0]->codeSpace()) + ":" + ids[0]->code(); ret += ")"; } return ret; } //! @endcond #endif // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress class InvalidOperationEmptyIntersection : public InvalidOperation { public: explicit InvalidOperationEmptyIntersection(const std::string &message); InvalidOperationEmptyIntersection( const InvalidOperationEmptyIntersection &other); ~InvalidOperationEmptyIntersection() override; }; InvalidOperationEmptyIntersection::InvalidOperationEmptyIntersection( const std::string &message) : InvalidOperation(message) {} InvalidOperationEmptyIntersection::InvalidOperationEmptyIntersection( const InvalidOperationEmptyIntersection &) = default; InvalidOperationEmptyIntersection::~InvalidOperationEmptyIntersection() = default; // --------------------------------------------------------------------------- static std::string createEntryEqParam(const std::string &a, const std::string &b) { return a < b ? a + b : b + a; } static std::set buildSetEquivalentParameters() { std::set set; const char *const listOfEquivalentParameterNames[][7] = { {"latitude_of_point_1", "Latitude_Of_1st_Point", nullptr}, {"longitude_of_point_1", "Longitude_Of_1st_Point", nullptr}, {"latitude_of_point_2", "Latitude_Of_2nd_Point", nullptr}, {"longitude_of_point_2", "Longitude_Of_2nd_Point", nullptr}, {"satellite_height", "height", nullptr}, {EPSG_NAME_PARAMETER_FALSE_EASTING, EPSG_NAME_PARAMETER_EASTING_FALSE_ORIGIN, EPSG_NAME_PARAMETER_EASTING_PROJECTION_CENTRE, nullptr}, {EPSG_NAME_PARAMETER_FALSE_NORTHING, EPSG_NAME_PARAMETER_NORTHING_FALSE_ORIGIN, EPSG_NAME_PARAMETER_NORTHING_PROJECTION_CENTRE, nullptr}, {EPSG_NAME_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN, WKT1_SCALE_FACTOR, EPSG_NAME_PARAMETER_SCALE_FACTOR_INITIAL_LINE, EPSG_NAME_PARAMETER_SCALE_FACTOR_PSEUDO_STANDARD_PARALLEL, nullptr}, {WKT1_LATITUDE_OF_ORIGIN, WKT1_LATITUDE_OF_CENTER, EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, EPSG_NAME_PARAMETER_LATITUDE_FALSE_ORIGIN, EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, "Central_Parallel", nullptr}, {WKT1_CENTRAL_MERIDIAN, WKT1_LONGITUDE_OF_CENTER, EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, EPSG_NAME_PARAMETER_LONGITUDE_FALSE_ORIGIN, EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, EPSG_NAME_PARAMETER_LONGITUDE_OF_ORIGIN, nullptr}, {"pseudo_standard_parallel_1", WKT1_STANDARD_PARALLEL_1, nullptr}, }; for (const auto ¶mList : listOfEquivalentParameterNames) { for (size_t i = 0; paramList[i]; i++) { auto a = metadata::Identifier::canonicalizeName(paramList[i]); for (size_t j = i + 1; paramList[j]; j++) { auto b = metadata::Identifier::canonicalizeName(paramList[j]); set.insert(createEntryEqParam(a, b)); } } } return set; } bool areEquivalentParameters(const std::string &a, const std::string &b) { static const std::set setEquivalentParameters = buildSetEquivalentParameters(); auto a_can = metadata::Identifier::canonicalizeName(a); auto b_can = metadata::Identifier::canonicalizeName(b); return setEquivalentParameters.find(createEntryEqParam(a_can, b_can)) != setEquivalentParameters.end(); } // --------------------------------------------------------------------------- PROJ_NO_INLINE const MethodMapping *getMapping(int epsg_code) noexcept { for (const auto &mapping : projectionMethodMappings) { if (mapping.epsg_code == epsg_code) { return &mapping; } } return nullptr; } // --------------------------------------------------------------------------- const MethodMapping *getMapping(const OperationMethod *method) noexcept { const std::string &name(method->nameStr()); const int epsg_code = method->getEPSGCode(); for (const auto &mapping : projectionMethodMappings) { if ((epsg_code != 0 && mapping.epsg_code == epsg_code) || metadata::Identifier::isEquivalentName(mapping.wkt2_name, name.c_str())) { return &mapping; } } return nullptr; } // --------------------------------------------------------------------------- const MethodMapping *getMappingFromWKT1(const std::string &wkt1_name) noexcept { // Unusual for a WKT1 projection name, but mentioned in OGC 12-063r5 C.4.2 if (ci_starts_with(wkt1_name, "UTM zone")) { return getMapping(EPSG_CODE_METHOD_TRANSVERSE_MERCATOR); } for (const auto &mapping : projectionMethodMappings) { if (mapping.wkt1_name && metadata::Identifier::isEquivalentName( mapping.wkt1_name, wkt1_name.c_str())) { return &mapping; } } return nullptr; } // --------------------------------------------------------------------------- const MethodMapping *getMapping(const char *wkt2_name) noexcept { for (const auto &mapping : projectionMethodMappings) { if (metadata::Identifier::isEquivalentName(mapping.wkt2_name, wkt2_name)) { return &mapping; } } for (const auto &mapping : otherMethodMappings) { if (metadata::Identifier::isEquivalentName(mapping.wkt2_name, wkt2_name)) { return &mapping; } } return nullptr; } // --------------------------------------------------------------------------- std::vector getMappingsFromPROJName(const std::string &projName) { std::vector res; for (const auto &mapping : projectionMethodMappings) { if (mapping.proj_name_main && projName == mapping.proj_name_main) { res.push_back(&mapping); } } return res; } // --------------------------------------------------------------------------- static const ParamMapping *getMapping(const MethodMapping *mapping, const OperationParameterNNPtr ¶m) { if (mapping->params == nullptr) { return nullptr; } // First try with id const int epsg_code = param->getEPSGCode(); if (epsg_code) { for (int i = 0; mapping->params[i] != nullptr; ++i) { const auto *paramMapping = mapping->params[i]; if (paramMapping->epsg_code == epsg_code) { return paramMapping; } } } // then equivalent name const std::string &name = param->nameStr(); for (int i = 0; mapping->params[i] != nullptr; ++i) { const auto *paramMapping = mapping->params[i]; if (metadata::Identifier::isEquivalentName(paramMapping->wkt2_name, name.c_str())) { return paramMapping; } } // and finally different name, but equivalent parameter for (int i = 0; mapping->params[i] != nullptr; ++i) { const auto *paramMapping = mapping->params[i]; if (areEquivalentParameters(paramMapping->wkt2_name, name)) { return paramMapping; } } return nullptr; } // --------------------------------------------------------------------------- const ParamMapping *getMappingFromWKT1(const MethodMapping *mapping, const std::string &wkt1_name) { for (int i = 0; mapping->params[i] != nullptr; ++i) { const auto *paramMapping = mapping->params[i]; if (paramMapping->wkt1_name && (metadata::Identifier::isEquivalentName(paramMapping->wkt1_name, wkt1_name.c_str()) || areEquivalentParameters(paramMapping->wkt1_name, wkt1_name))) { return paramMapping; } } return nullptr; } // --------------------------------------------------------------------------- std::vector getMappingsFromESRI(const std::string &esri_name) { std::vector res; for (const auto &mapping : esriMappings) { if (ci_equal(esri_name, mapping.esri_name)) { res.push_back(&mapping); } } return res; } // --------------------------------------------------------------------------- static const ESRIMethodMapping *getESRIMapping(const std::string &wkt2_name, int epsg_code) { for (const auto &mapping : esriMappings) { if ((epsg_code != 0 && mapping.epsg_code == epsg_code) || ci_equal(wkt2_name, mapping.wkt2_name)) { return &mapping; } } return nullptr; } // --------------------------------------------------------------------------- static double getAccuracy(const std::vector &ops); // Returns the accuracy of an operation, or -1 if unknown static double getAccuracy(const CoordinateOperationNNPtr &op) { if (dynamic_cast(op.get())) { // A conversion is perfectly accurate. return 0.0; } double accuracy = -1.0; const auto &accuracies = op->coordinateOperationAccuracies(); if (!accuracies.empty()) { try { accuracy = c_locale_stod(accuracies[0]->value()); } catch (const std::exception &) { } } else { auto concatenated = dynamic_cast(op.get()); if (concatenated) { accuracy = getAccuracy(concatenated->operations()); } } return accuracy; } // --------------------------------------------------------------------------- // Returns the accuracy of a set of concatenated operations, or -1 if unknown static double getAccuracy(const std::vector &ops) { double accuracy = -1.0; for (const auto &subop : ops) { const double subops_accuracy = getAccuracy(subop); if (subops_accuracy < 0.0) { return -1.0; } if (accuracy < 0.0) { accuracy = 0.0; } accuracy += subops_accuracy; } return accuracy; } // --------------------------------------------------------------------------- static metadata::ExtentPtr getExtent(const std::vector &ops, bool conversionExtentIsWorld, bool &emptyIntersection); static metadata::ExtentPtr getExtent(const CoordinateOperationNNPtr &op, bool conversionExtentIsWorld, bool &emptyIntersection) { auto conv = dynamic_cast(op.get()); if (conv) { emptyIntersection = false; return metadata::Extent::WORLD; } const auto &domains = op->domains(); if (!domains.empty()) { emptyIntersection = false; return domains[0]->domainOfValidity(); } auto concatenated = dynamic_cast(op.get()); if (!concatenated) { emptyIntersection = false; return nullptr; } return getExtent(concatenated->operations(), conversionExtentIsWorld, emptyIntersection); } // --------------------------------------------------------------------------- static const metadata::ExtentPtr nullExtent{}; static const metadata::ExtentPtr &getExtent(const crs::CRSNNPtr &crs) { const auto &domains = crs->domains(); if (!domains.empty()) { return domains[0]->domainOfValidity(); } const auto *boundCRS = dynamic_cast(crs.get()); if (boundCRS) { return getExtent(boundCRS->baseCRS()); } return nullExtent; } static const metadata::ExtentPtr getExtentPossiblySynthetized(const crs::CRSNNPtr &crs, bool &approxOut) { const auto &rawExtent(getExtent(crs)); approxOut = false; if (rawExtent) return rawExtent; const auto compoundCRS = dynamic_cast(crs.get()); if (compoundCRS) { // For a compoundCRS, take the intersection of the extent of its // components. const auto &components = compoundCRS->componentReferenceSystems(); metadata::ExtentPtr extent; approxOut = true; for (const auto &component : components) { const auto &componentExtent(getExtent(component)); if (extent && componentExtent) extent = extent->intersection(NN_NO_CHECK(componentExtent)); else if (componentExtent) extent = componentExtent; } return extent; } return rawExtent; } // --------------------------------------------------------------------------- static metadata::ExtentPtr getExtent(const std::vector &ops, bool conversionExtentIsWorld, bool &emptyIntersection) { metadata::ExtentPtr res = nullptr; for (const auto &subop : ops) { const auto &subExtent = getExtent(subop, conversionExtentIsWorld, emptyIntersection); if (!subExtent) { if (emptyIntersection) { return nullptr; } continue; } if (res == nullptr) { res = subExtent; } else { res = res->intersection(NN_NO_CHECK(subExtent)); if (!res) { emptyIntersection = true; return nullptr; } } } emptyIntersection = false; return res; } // --------------------------------------------------------------------------- static double getPseudoArea(const metadata::ExtentPtr &extent) { if (!extent) return 0.0; const auto &geographicElements = extent->geographicElements(); if (geographicElements.empty()) return 0.0; auto bbox = dynamic_cast( geographicElements[0].get()); if (!bbox) return 0; double w = bbox->westBoundLongitude(); double s = bbox->southBoundLatitude(); double e = bbox->eastBoundLongitude(); double n = bbox->northBoundLatitude(); if (w > e) { e += 360.0; } // Integrate cos(lat) between south_lat and north_lat return (e - w) * (std::sin(common::Angle(n).getSIValue()) - std::sin(common::Angle(s).getSIValue())); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CoordinateOperation::Private { util::optional operationVersion_{}; std::vector coordinateOperationAccuracies_{}; std::weak_ptr sourceCRSWeak_{}; std::weak_ptr targetCRSWeak_{}; crs::CRSPtr interpolationCRS_{}; util::optional sourceCoordinateEpoch_{}; util::optional targetCoordinateEpoch_{}; bool hasBallparkTransformation_ = false; // do not set this for a ProjectedCRS.definingConversion struct CRSStrongRef { crs::CRSNNPtr sourceCRS_; crs::CRSNNPtr targetCRS_; CRSStrongRef(const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn) : sourceCRS_(sourceCRSIn), targetCRS_(targetCRSIn) {} }; std::unique_ptr strongRef_{}; Private() = default; Private(const Private &other) : operationVersion_(other.operationVersion_), coordinateOperationAccuracies_(other.coordinateOperationAccuracies_), sourceCRSWeak_(other.sourceCRSWeak_), targetCRSWeak_(other.targetCRSWeak_), interpolationCRS_(other.interpolationCRS_), sourceCoordinateEpoch_(other.sourceCoordinateEpoch_), targetCoordinateEpoch_(other.targetCoordinateEpoch_), strongRef_(other.strongRef_ ? internal::make_unique( *(other.strongRef_)) : nullptr) {} Private &operator=(const Private &) = delete; }; // --------------------------------------------------------------------------- GridDescription::GridDescription() : shortName{}, fullName{}, packageName{}, url{}, directDownload(false), openLicense(false), available(false) {} GridDescription::~GridDescription() = default; GridDescription::GridDescription(const GridDescription &) = default; GridDescription::GridDescription(GridDescription &&other) noexcept : shortName(std::move(other.shortName)), fullName(std::move(other.fullName)), packageName(std::move(other.packageName)), url(std::move(other.url)), directDownload(other.directDownload), openLicense(other.openLicense), available(other.available) {} //! @endcond // --------------------------------------------------------------------------- CoordinateOperation::CoordinateOperation() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- CoordinateOperation::CoordinateOperation(const CoordinateOperation &other) : ObjectUsage(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateOperation::~CoordinateOperation() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the version of the coordinate transformation (i.e. * instantiation * due to the stochastic nature of the parameters). * * Mandatory when describing a coordinate transformation or point motion * operation, and should not be supplied for a coordinate conversion. * * @return version or empty. */ const util::optional & CoordinateOperation::operationVersion() const { return d->operationVersion_; } // --------------------------------------------------------------------------- /** \brief Return estimate(s) of the impact of this coordinate operation on * point accuracy. * * Gives position error estimates for target coordinates of this coordinate * operation, assuming no errors in source coordinates. * * @return estimate(s) or empty vector. */ const std::vector & CoordinateOperation::coordinateOperationAccuracies() const { return d->coordinateOperationAccuracies_; } // --------------------------------------------------------------------------- /** \brief Return the source CRS of this coordinate operation. * * This should not be null, expect for of a derivingConversion of a DerivedCRS * when the owning DerivedCRS has been destroyed. * * @return source CRS, or null. */ const crs::CRSPtr CoordinateOperation::sourceCRS() const { return d->sourceCRSWeak_.lock(); } // --------------------------------------------------------------------------- /** \brief Return the target CRS of this coordinate operation. * * This should not be null, expect for of a derivingConversion of a DerivedCRS * when the owning DerivedCRS has been destroyed. * * @return target CRS, or null. */ const crs::CRSPtr CoordinateOperation::targetCRS() const { return d->targetCRSWeak_.lock(); } // --------------------------------------------------------------------------- /** \brief Return the interpolation CRS of this coordinate operation. * * @return interpolation CRS, or null. */ const crs::CRSPtr &CoordinateOperation::interpolationCRS() const { return d->interpolationCRS_; } // --------------------------------------------------------------------------- /** \brief Return the source epoch of coordinates. * * @return source epoch of coordinates, or empty. */ const util::optional & CoordinateOperation::sourceCoordinateEpoch() const { return d->sourceCoordinateEpoch_; } // --------------------------------------------------------------------------- /** \brief Return the target epoch of coordinates. * * @return target epoch of coordinates, or empty. */ const util::optional & CoordinateOperation::targetCoordinateEpoch() const { return d->targetCoordinateEpoch_; } // --------------------------------------------------------------------------- void CoordinateOperation::setWeakSourceTargetCRS( std::weak_ptr sourceCRSIn, std::weak_ptr targetCRSIn) { d->sourceCRSWeak_ = sourceCRSIn; d->targetCRSWeak_ = targetCRSIn; } // --------------------------------------------------------------------------- void CoordinateOperation::setCRSs(const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn) { d->strongRef_ = internal::make_unique(sourceCRSIn, targetCRSIn); d->sourceCRSWeak_ = sourceCRSIn.as_nullable(); d->targetCRSWeak_ = targetCRSIn.as_nullable(); d->interpolationCRS_ = interpolationCRSIn; } // --------------------------------------------------------------------------- void CoordinateOperation::setCRSs(const CoordinateOperation *in, bool inverseSourceTarget) { auto l_sourceCRS = in->sourceCRS(); auto l_targetCRS = in->targetCRS(); if (l_sourceCRS && l_targetCRS) { auto nn_sourceCRS = NN_NO_CHECK(l_sourceCRS); auto nn_targetCRS = NN_NO_CHECK(l_targetCRS); if (inverseSourceTarget) { setCRSs(nn_targetCRS, nn_sourceCRS, in->interpolationCRS()); } else { setCRSs(nn_sourceCRS, nn_targetCRS, in->interpolationCRS()); } } } // --------------------------------------------------------------------------- void CoordinateOperation::setAccuracies( const std::vector &accuracies) { d->coordinateOperationAccuracies_ = accuracies; } // --------------------------------------------------------------------------- /** \brief Return whether a coordinate operation can be instantiated as * a PROJ pipeline, checking in particular that referenced grids are * available. */ bool CoordinateOperation::isPROJInstantiable( const io::DatabaseContextPtr &databaseContext) const { try { exportToPROJString(io::PROJStringFormatter::create().get()); } catch (const std::exception &) { return false; } for (const auto &gridDesc : gridsNeeded(databaseContext)) { if (!gridDesc.available) { return false; } } return true; } // --------------------------------------------------------------------------- /** \brief Return whether a coordinate operation has a "ballpark" * transformation, * that is a very approximate one, due to lack of more accurate transformations. * * Typically a null geographic offset between two horizontal datum, or a * null vertical offset (or limited to unit changes) between two vertical * datum. Errors of several tens to one hundred meters might be expected, * compared to more accurate transformations. */ bool CoordinateOperation::hasBallparkTransformation() const { return d->hasBallparkTransformation_; } // --------------------------------------------------------------------------- void CoordinateOperation::setHasBallparkTransformation(bool b) { d->hasBallparkTransformation_ = b; } // --------------------------------------------------------------------------- void CoordinateOperation::setProperties( const util::PropertyMap &properties) // throw(InvalidValueTypeException) { ObjectUsage::setProperties(properties); properties.getStringValue(OPERATION_VERSION_KEY, d->operationVersion_); } // --------------------------------------------------------------------------- /** \brief Return a variation of the current coordinate operation whose axis * order is the one expected for visualization purposes. */ CoordinateOperationNNPtr CoordinateOperation::normalizeForVisualization() const { auto l_sourceCRS = sourceCRS(); auto l_targetCRS = targetCRS(); if (!l_sourceCRS || !l_targetCRS) { throw util::UnsupportedOperationException( "Cannot retrieve source or target CRS"); } const bool swapSource = l_sourceCRS->mustAxisOrderBeSwitchedForVisualization(); const bool swapTarget = l_targetCRS->mustAxisOrderBeSwitchedForVisualization(); auto l_this = NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); if (!swapSource && !swapTarget) { return l_this; } std::vector subOps; if (swapSource) { auto op = Conversion::createAxisOrderReversal(false); op->setCRSs(l_sourceCRS->normalizeForVisualization(), NN_NO_CHECK(l_sourceCRS), nullptr); subOps.emplace_back(op); } subOps.emplace_back(l_this); if (swapTarget) { auto op = Conversion::createAxisOrderReversal(false); op->setCRSs(NN_NO_CHECK(l_targetCRS), l_targetCRS->normalizeForVisualization(), nullptr); subOps.emplace_back(op); } return util::nn_static_pointer_cast( ConcatenatedOperation::createComputeMetadata(subOps, true)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateOperationNNPtr CoordinateOperation::shallowClone() const { return _shallowClone(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct OperationMethod::Private { util::optional formula_{}; util::optional formulaCitation_{}; std::vector parameters_{}; std::string projMethodOverride_{}; }; //! @endcond // --------------------------------------------------------------------------- OperationMethod::OperationMethod() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- OperationMethod::OperationMethod(const OperationMethod &other) : IdentifiedObject(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress OperationMethod::~OperationMethod() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the formula(s) or procedure used by this coordinate operation * method. * * This may be a reference to a publication (in which case use * formulaCitation()). * * Note that the operation method may not be analytic, in which case this * attribute references or contains the procedure, not an analytic formula. * * @return the formula, or empty. */ const util::optional &OperationMethod::formula() PROJ_PURE_DEFN { return d->formula_; } // --------------------------------------------------------------------------- /** \brief Return a reference to a publication giving the formula(s) or * procedure * used by the coordinate operation method. * * @return the formula citation, or empty. */ const util::optional & OperationMethod::formulaCitation() PROJ_PURE_DEFN { return d->formulaCitation_; } // --------------------------------------------------------------------------- /** \brief Return the parameters of this operation method. * * @return the parameters. */ const std::vector & OperationMethod::parameters() PROJ_PURE_DEFN { return d->parameters_; } // --------------------------------------------------------------------------- /** \brief Instantiate a operation method from a vector of * GeneralOperationParameter. * * @param properties See \ref general_properties. At minimum the name should be * defined. * @param parameters Vector of GeneralOperationParameterNNPtr. * @return a new OperationMethod. */ OperationMethodNNPtr OperationMethod::create( const util::PropertyMap &properties, const std::vector ¶meters) { OperationMethodNNPtr method( OperationMethod::nn_make_shared()); method->assignSelf(method); method->setProperties(properties); method->d->parameters_ = parameters; properties.getStringValue("proj_method", method->d->projMethodOverride_); return method; } // --------------------------------------------------------------------------- /** \brief Instantiate a operation method from a vector of OperationParameter. * * @param properties See \ref general_properties. At minimum the name should be * defined. * @param parameters Vector of OperationParameterNNPtr. * @return a new OperationMethod. */ OperationMethodNNPtr OperationMethod::create( const util::PropertyMap &properties, const std::vector ¶meters) { std::vector parametersGeneral; parametersGeneral.reserve(parameters.size()); for (const auto &p : parameters) { parametersGeneral.push_back(p); } return create(properties, parametersGeneral); } // --------------------------------------------------------------------------- /** \brief Return the EPSG code, either directly, or through the name * @return code, or 0 if not found */ int OperationMethod::getEPSGCode() PROJ_PURE_DEFN { int epsg_code = IdentifiedObject::getEPSGCode(); if (epsg_code == 0) { auto l_name = nameStr(); if (ends_with(l_name, " (3D)")) { l_name.resize(l_name.size() - strlen(" (3D)")); } for (const auto &tuple : methodNameCodes) { if (metadata::Identifier::isEquivalentName(l_name.c_str(), tuple.name)) { return tuple.epsg_code; } } } return epsg_code; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void OperationMethod::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; formatter->startNode(isWKT2 ? io::WKTConstants::METHOD : io::WKTConstants::PROJECTION, !identifiers().empty()); std::string l_name(nameStr()); if (!isWKT2) { const MethodMapping *mapping = getMapping(this); if (mapping == nullptr) { l_name = replaceAll(l_name, " ", "_"); } else { if (l_name == PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X) { l_name = "Geostationary_Satellite"; } else { if (mapping->wkt1_name == nullptr) { throw io::FormattingException( std::string("Unsupported conversion method: ") + mapping->wkt2_name); } l_name = mapping->wkt1_name; } } } formatter->addQuotedString(l_name); if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void OperationMethod::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext("OperationMethod", !identifiers().empty())); writer.AddObjKey("name"); writer.Add(nameStr()); if (formatter->outputId()) { formatID(formatter); } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool OperationMethod::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherOM = dynamic_cast(other); if (otherOM == nullptr || !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return false; } // TODO test formula and formulaCitation const auto ¶ms = parameters(); const auto &otherParams = otherOM->parameters(); const auto paramsSize = params.size(); if (paramsSize != otherParams.size()) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { for (size_t i = 0; i < paramsSize; i++) { if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion, dbContext)) { return false; } } } else { std::vector candidateIndices(paramsSize, true); for (size_t i = 0; i < paramsSize; i++) { bool found = false; for (size_t j = 0; j < paramsSize; j++) { if (candidateIndices[j] && params[i]->_isEquivalentTo(otherParams[j].get(), criterion, dbContext)) { candidateIndices[j] = false; found = true; break; } } if (!found) { return false; } } } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeneralParameterValue::Private {}; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeneralParameterValue::GeneralParameterValue() : d(nullptr) {} // --------------------------------------------------------------------------- GeneralParameterValue::GeneralParameterValue(const GeneralParameterValue &) : d(nullptr) {} //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeneralParameterValue::~GeneralParameterValue() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct OperationParameterValue::Private { OperationParameterNNPtr parameter; ParameterValueNNPtr parameterValue; Private(const OperationParameterNNPtr ¶meterIn, const ParameterValueNNPtr &valueIn) : parameter(parameterIn), parameterValue(valueIn) {} }; //! @endcond // --------------------------------------------------------------------------- OperationParameterValue::OperationParameterValue( const OperationParameterValue &other) : GeneralParameterValue(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- OperationParameterValue::OperationParameterValue( const OperationParameterNNPtr ¶meterIn, const ParameterValueNNPtr &valueIn) : GeneralParameterValue(), d(internal::make_unique(parameterIn, valueIn)) {} // --------------------------------------------------------------------------- /** \brief Instantiate a OperationParameterValue. * * @param parameterIn Parameter (definition). * @param valueIn Parameter value. * @return a new OperationParameterValue. */ OperationParameterValueNNPtr OperationParameterValue::create(const OperationParameterNNPtr ¶meterIn, const ParameterValueNNPtr &valueIn) { return OperationParameterValue::nn_make_shared( parameterIn, valueIn); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress OperationParameterValue::~OperationParameterValue() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the parameter (definition) * * @return the parameter (definition). */ const OperationParameterNNPtr & OperationParameterValue::parameter() PROJ_PURE_DEFN { return d->parameter; } // --------------------------------------------------------------------------- /** \brief Return the parameter value. * * @return the parameter value. */ const ParameterValueNNPtr & OperationParameterValue::parameterValue() PROJ_PURE_DEFN { return d->parameterValue; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void OperationParameterValue::_exportToWKT( // cppcheck-suppress passedByValue io::WKTFormatter *formatter) const { _exportToWKT(formatter, nullptr); } void OperationParameterValue::_exportToWKT(io::WKTFormatter *formatter, const MethodMapping *mapping) const { const ParamMapping *paramMapping = mapping ? getMapping(mapping, d->parameter) : nullptr; if (paramMapping && paramMapping->wkt1_name == nullptr) { return; } const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (isWKT2 && parameterValue()->type() == ParameterValue::Type::FILENAME) { formatter->startNode(io::WKTConstants::PARAMETERFILE, !parameter()->identifiers().empty()); } else { formatter->startNode(io::WKTConstants::PARAMETER, !parameter()->identifiers().empty()); } if (paramMapping) { formatter->addQuotedString(paramMapping->wkt1_name); } else { formatter->addQuotedString(parameter()->nameStr()); } parameterValue()->_exportToWKT(formatter); if (formatter->outputId()) { parameter()->formatID(formatter); } formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void OperationParameterValue::_exportToJSON( io::JSONFormatter *formatter) const { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext( "ParameterValue", !parameter()->identifiers().empty())); writer.AddObjKey("name"); writer.Add(parameter()->nameStr()); const auto &l_value(parameterValue()); if (l_value->type() == ParameterValue::Type::MEASURE) { writer.AddObjKey("value"); writer.Add(l_value->value().value(), 15); writer.AddObjKey("unit"); const auto &l_unit(l_value->value().unit()); if (l_unit == common::UnitOfMeasure::METRE || l_unit == common::UnitOfMeasure::DEGREE || l_unit == common::UnitOfMeasure::SCALE_UNITY) { writer.Add(l_unit.name()); } else { l_unit._exportToJSON(formatter); } } else if (l_value->type() == ParameterValue::Type::FILENAME) { writer.AddObjKey("value"); writer.Add(l_value->valueFile()); } if (formatter->outputId()) { parameter()->formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress /** Utility method used on WKT2 import to convert from abridged transformation * to "normal" transformation parameters. */ bool OperationParameterValue::convertFromAbridged( const std::string ¶mName, double &val, const common::UnitOfMeasure *&unit, int ¶mEPSGCode) { if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION) || paramEPSGCode == EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION) { unit = &common::UnitOfMeasure::METRE; paramEPSGCode = EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION) || paramEPSGCode == EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION) { unit = &common::UnitOfMeasure::METRE; paramEPSGCode = EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION) || paramEPSGCode == EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION) { unit = &common::UnitOfMeasure::METRE; paramEPSGCode = EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_X_AXIS_ROTATION) || paramEPSGCode == EPSG_CODE_PARAMETER_X_AXIS_ROTATION) { unit = &common::UnitOfMeasure::ARC_SECOND; paramEPSGCode = EPSG_CODE_PARAMETER_X_AXIS_ROTATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_Y_AXIS_ROTATION) || paramEPSGCode == EPSG_CODE_PARAMETER_Y_AXIS_ROTATION) { unit = &common::UnitOfMeasure::ARC_SECOND; paramEPSGCode = EPSG_CODE_PARAMETER_Y_AXIS_ROTATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_Z_AXIS_ROTATION) || paramEPSGCode == EPSG_CODE_PARAMETER_Z_AXIS_ROTATION) { unit = &common::UnitOfMeasure::ARC_SECOND; paramEPSGCode = EPSG_CODE_PARAMETER_Z_AXIS_ROTATION; return true; } else if (metadata::Identifier::isEquivalentName( paramName.c_str(), EPSG_NAME_PARAMETER_SCALE_DIFFERENCE) || paramEPSGCode == EPSG_CODE_PARAMETER_SCALE_DIFFERENCE) { val = (val - 1.0) * 1e6; unit = &common::UnitOfMeasure::PARTS_PER_MILLION; paramEPSGCode = EPSG_CODE_PARAMETER_SCALE_DIFFERENCE; return true; } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool OperationParameterValue::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherOPV = dynamic_cast(other); if (otherOPV == nullptr) { return false; } if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), criterion, dbContext)) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { return d->parameterValue->_isEquivalentTo( otherOPV->d->parameterValue.get(), criterion); } if (d->parameterValue->_isEquivalentTo(otherOPV->d->parameterValue.get(), criterion, dbContext)) { return true; } if (d->parameter->getEPSGCode() == EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE || d->parameter->getEPSGCode() == EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID) { if (parameterValue()->type() == ParameterValue::Type::MEASURE && otherOPV->parameterValue()->type() == ParameterValue::Type::MEASURE) { const double a = std::fmod(parameterValue()->value().convertToUnit( common::UnitOfMeasure::DEGREE) + 360.0, 360.0); const double b = std::fmod(otherOPV->parameterValue()->value().convertToUnit( common::UnitOfMeasure::DEGREE) + 360.0, 360.0); return std::fabs(a - b) <= 1e-10 * std::fabs(a); } } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct GeneralOperationParameter::Private {}; //! @endcond // --------------------------------------------------------------------------- GeneralOperationParameter::GeneralOperationParameter() : d(nullptr) {} // --------------------------------------------------------------------------- GeneralOperationParameter::GeneralOperationParameter( const GeneralOperationParameter &other) : IdentifiedObject(other), d(nullptr) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress GeneralOperationParameter::~GeneralOperationParameter() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct OperationParameter::Private {}; //! @endcond // --------------------------------------------------------------------------- OperationParameter::OperationParameter() : d(nullptr) {} // --------------------------------------------------------------------------- OperationParameter::OperationParameter(const OperationParameter &other) : GeneralOperationParameter(other), d(nullptr) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress OperationParameter::~OperationParameter() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a OperationParameter. * * @param properties See \ref general_properties. At minimum the name should be * defined. * @return a new OperationParameter. */ OperationParameterNNPtr OperationParameter::create(const util::PropertyMap &properties) { OperationParameterNNPtr op( OperationParameter::nn_make_shared()); op->assignSelf(op); op->setProperties(properties); return op; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool OperationParameter::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherOP = dynamic_cast(other); if (otherOP == nullptr) { return false; } if (criterion == util::IComparable::Criterion::STRICT) { return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } if (IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) { return true; } auto l_epsgCode = getEPSGCode(); return l_epsgCode != 0 && l_epsgCode == otherOP->getEPSGCode(); } //! @endcond // --------------------------------------------------------------------------- void OperationParameter::_exportToWKT(io::WKTFormatter *) const {} // --------------------------------------------------------------------------- /** \brief Return the name of a parameter designed by its EPSG code * @return name, or nullptr if not found */ const char *OperationParameter::getNameForEPSGCode(int epsg_code) noexcept { for (const auto &tuple : paramNameCodes) { if (tuple.epsg_code == epsg_code) { return tuple.name; } } return nullptr; } // --------------------------------------------------------------------------- /** \brief Return the EPSG code, either directly, or through the name * @return code, or 0 if not found */ int OperationParameter::getEPSGCode() PROJ_PURE_DEFN { int epsg_code = IdentifiedObject::getEPSGCode(); if (epsg_code == 0) { const auto &l_name = nameStr(); for (const auto &tuple : paramNameCodes) { if (metadata::Identifier::isEquivalentName(l_name.c_str(), tuple.name)) { return tuple.epsg_code; } } if (metadata::Identifier::isEquivalentName(l_name.c_str(), "Latitude of origin")) { return EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN; } if (metadata::Identifier::isEquivalentName(l_name.c_str(), "Scale factor")) { return EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN; } } return epsg_code; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct SingleOperation::Private { std::vector parameterValues_{}; OperationMethodNNPtr method_; explicit Private(const OperationMethodNNPtr &methodIn) : method_(methodIn) {} }; //! @endcond // --------------------------------------------------------------------------- SingleOperation::SingleOperation(const OperationMethodNNPtr &methodIn) : d(internal::make_unique(methodIn)) {} // --------------------------------------------------------------------------- SingleOperation::SingleOperation(const SingleOperation &other) : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT) CoordinateOperation(other), #endif d(internal::make_unique(*other.d)) { } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress SingleOperation::~SingleOperation() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the parameter values. * * @return the parameter values. */ const std::vector & SingleOperation::parameterValues() PROJ_PURE_DEFN { return d->parameterValues_; } // --------------------------------------------------------------------------- /** \brief Return the operation method associated to the operation. * * @return the operation method. */ const OperationMethodNNPtr &SingleOperation::method() PROJ_PURE_DEFN { return d->method_; } // --------------------------------------------------------------------------- void SingleOperation::setParameterValues( const std::vector &values) { d->parameterValues_ = values; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const ParameterValuePtr nullParameterValue; //! @endcond /** \brief Return the parameter value corresponding to a parameter name or * EPSG code * * @param paramName the parameter name (or empty, in which case epsg_code * should be non zero) * @param epsg_code the parameter EPSG code (possibly zero) * @return the value, or nullptr if not found. */ const ParameterValuePtr & SingleOperation::parameterValue(const std::string ¶mName, int epsg_code) const noexcept { if (epsg_code) { for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if (parameter->getEPSGCode() == epsg_code) { return opParamvalue->parameterValue(); } } } } for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if (metadata::Identifier::isEquivalentName( paramName.c_str(), parameter->nameStr().c_str())) { return opParamvalue->parameterValue(); } } } for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if (areEquivalentParameters(paramName, parameter->nameStr())) { return opParamvalue->parameterValue(); } } } return nullParameterValue; } // --------------------------------------------------------------------------- /** \brief Return the parameter value corresponding to a EPSG code * * @param epsg_code the parameter EPSG code * @return the value, or nullptr if not found. */ const ParameterValuePtr &SingleOperation::parameterValue(int epsg_code) const noexcept { for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if (parameter->getEPSGCode() == epsg_code) { return opParamvalue->parameterValue(); } } } return nullParameterValue; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const common::Measure nullMeasure; //! @endcond /** \brief Return the parameter value, as a measure, corresponding to a * parameter name or EPSG code * * @param paramName the parameter name (or empty, in which case epsg_code * should be non zero) * @param epsg_code the parameter EPSG code (possibly zero) * @return the measure, or the empty Measure() object if not found. */ const common::Measure & SingleOperation::parameterValueMeasure(const std::string ¶mName, int epsg_code) const noexcept { const auto &val = parameterValue(paramName, epsg_code); if (val && val->type() == ParameterValue::Type::MEASURE) { return val->value(); } return nullMeasure; } /** \brief Return the parameter value, as a measure, corresponding to a * EPSG code * * @param epsg_code the parameter EPSG code * @return the measure, or the empty Measure() object if not found. */ const common::Measure & SingleOperation::parameterValueMeasure(int epsg_code) const noexcept { const auto &val = parameterValue(epsg_code); if (val && val->type() == ParameterValue::Type::MEASURE) { return val->value(); } return nullMeasure; } //! @cond Doxygen_Suppress double SingleOperation::parameterValueNumericAsSI(int epsg_code) const noexcept { const auto &val = parameterValue(epsg_code); if (val && val->type() == ParameterValue::Type::MEASURE) { return val->value().getSIValue(); } return 0.0; } double SingleOperation::parameterValueNumeric( int epsg_code, const common::UnitOfMeasure &targetUnit) const noexcept { const auto &val = parameterValue(epsg_code); if (val && val->type() == ParameterValue::Type::MEASURE) { return val->value().convertToUnit(targetUnit); } return 0.0; } double SingleOperation::parameterValueNumeric( const char *param_name, const common::UnitOfMeasure &targetUnit) const noexcept { const auto &val = parameterValue(param_name, 0); if (val && val->type() == ParameterValue::Type::MEASURE) { return val->value().convertToUnit(targetUnit); } return 0.0; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a PROJ-based single operation. * * \note The operation might internally be a pipeline chaining several * operations. * The use of the SingleOperation modeling here is mostly to be able to get * the PROJ string as a parameter. * * @param properties Properties * @param PROJString the PROJ string. * @param sourceCRS source CRS (might be null). * @param targetCRS target CRS (might be null). * @param accuracies Vector of positional accuracy (might be empty). * @return the new instance */ SingleOperationNNPtr SingleOperation::createPROJBased( const util::PropertyMap &properties, const std::string &PROJString, const crs::CRSPtr &sourceCRS, const crs::CRSPtr &targetCRS, const std::vector &accuracies) { return util::nn_static_pointer_cast( PROJBasedOperation::create(properties, PROJString, sourceCRS, targetCRS, accuracies)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static SingleOperationNNPtr createPROJBased( const util::PropertyMap &properties, const io::IPROJStringExportableNNPtr &projExportable, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const std::vector &accuracies, bool hasBallparkTransformation) { return util::nn_static_pointer_cast( PROJBasedOperation::create(properties, projExportable, false, sourceCRS, targetCRS, accuracies, hasBallparkTransformation)); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool SingleOperation::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { return _isEquivalentTo(other, criterion, dbContext, false); } bool SingleOperation::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext, bool inOtherDirection) const { auto otherSO = dynamic_cast(other); if (otherSO == nullptr || (criterion == util::IComparable::Criterion::STRICT && !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const int methodEPSGCode = d->method_->getEPSGCode(); const int otherMethodEPSGCode = otherSO->d->method_->getEPSGCode(); bool equivalentMethods = (criterion == util::IComparable::Criterion::EQUIVALENT && methodEPSGCode != 0 && methodEPSGCode == otherMethodEPSGCode) || d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion, dbContext); if (!equivalentMethods && criterion == util::IComparable::Criterion::EQUIVALENT) { if ((methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA && otherMethodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL) || (otherMethodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA && methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA_SPHERICAL) || (methodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL && otherMethodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL) || (otherMethodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL && methodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL)) { auto geodCRS = dynamic_cast(sourceCRS().get()); auto otherGeodCRS = dynamic_cast( otherSO->sourceCRS().get()); if (geodCRS && otherGeodCRS && geodCRS->ellipsoid()->isSphere() && otherGeodCRS->ellipsoid()->isSphere()) { equivalentMethods = true; } } } if (!equivalentMethods) { if (criterion == util::IComparable::Criterion::EQUIVALENT) { const auto isTOWGS84Transf = [](int code) { return code == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC || code == EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC || code == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC || code == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D || code == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D || code == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D || code == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D || code == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D || code == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D; }; // Translation vs (PV or CF) // or different PV vs CF convention if (isTOWGS84Transf(methodEPSGCode) && isTOWGS84Transf(otherMethodEPSGCode)) { auto transf = static_cast(this); auto otherTransf = static_cast(otherSO); auto params = transf->getTOWGS84Parameters(); auto otherParams = otherTransf->getTOWGS84Parameters(); assert(params.size() == 7); assert(otherParams.size() == 7); for (size_t i = 0; i < 7; i++) { if (std::fabs(params[i] - otherParams[i]) > 1e-10 * std::fabs(params[i])) { return false; } } return true; } // _1SP methods can sometimes be equivalent to _2SP ones // Check it by using convertToOtherMethod() if (methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP && otherMethodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { // Convert from 2SP to 1SP as the other direction has more // degree of liberties. return otherSO->_isEquivalentTo(this, criterion, dbContext); } else if ((methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A && otherMethodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B) || (methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && otherMethodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A) || (methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP && otherMethodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) { auto conv = dynamic_cast(this); if (conv) { auto eqConv = conv->convertToOtherMethod(otherMethodEPSGCode); if (eqConv) { return eqConv->_isEquivalentTo(other, criterion, dbContext); } } } } return false; } const auto &values = d->parameterValues_; const auto &otherValues = otherSO->d->parameterValues_; const auto valuesSize = values.size(); const auto otherValuesSize = otherValues.size(); if (criterion == util::IComparable::Criterion::STRICT) { if (valuesSize != otherValuesSize) { return false; } for (size_t i = 0; i < valuesSize; i++) { if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion, dbContext)) { return false; } } return true; } std::vector candidateIndices(otherValuesSize, true); bool equivalent = true; bool foundMissingArgs = valuesSize != otherValuesSize; for (size_t i = 0; equivalent && i < valuesSize; i++) { auto opParamvalue = dynamic_cast(values[i].get()); if (!opParamvalue) return false; equivalent = false; bool sameNameDifferentValue = false; for (size_t j = 0; j < otherValuesSize; j++) { if (candidateIndices[j] && values[i]->_isEquivalentTo(otherValues[j].get(), criterion, dbContext)) { candidateIndices[j] = false; equivalent = true; break; } else if (candidateIndices[j]) { auto otherOpParamvalue = dynamic_cast( otherValues[j].get()); if (!otherOpParamvalue) return false; sameNameDifferentValue = opParamvalue->parameter()->_isEquivalentTo( otherOpParamvalue->parameter().get(), criterion, dbContext); if (sameNameDifferentValue) { candidateIndices[j] = false; break; } } } if (!equivalent && methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { // For LCC_2SP, the standard parallels can be switched and // this will result in the same result. const int paramEPSGCode = opParamvalue->parameter()->getEPSGCode(); if (paramEPSGCode == EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL || paramEPSGCode == EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) { auto value_1st = parameterValue( EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL); auto value_2nd = parameterValue( EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL); if (value_1st && value_2nd) { equivalent = value_1st->_isEquivalentTo( otherSO ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) .get(), criterion, dbContext) && value_2nd->_isEquivalentTo( otherSO ->parameterValue( EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL) .get(), criterion, dbContext); } } } if (equivalent) { continue; } if (sameNameDifferentValue) { break; } // If there are parameters in this method not found in the other one, // check that they are set to a default neutral value, that is 1 // for scale, and 0 otherwise. foundMissingArgs = true; const auto &value = opParamvalue->parameterValue(); if (value->type() != ParameterValue::Type::MEASURE) { break; } if (value->value().unit().type() == common::UnitOfMeasure::Type::SCALE) { equivalent = value->value().getSIValue() == 1.0; } else { equivalent = value->value().getSIValue() == 0.0; } } // In the case the arguments don't perfectly match, try the reverse // check. if (equivalent && foundMissingArgs && !inOtherDirection) { return otherSO->_isEquivalentTo(this, criterion, dbContext, true); } // Equivalent formulations of 2SP can have different parameters // Then convert to 1SP and compare. if (!equivalent && methodEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { auto conv = dynamic_cast(this); auto otherConv = dynamic_cast(other); if (conv && otherConv) { auto thisAs1SP = conv->convertToOtherMethod( EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); auto otherAs1SP = otherConv->convertToOtherMethod( EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP); if (thisAs1SP && otherAs1SP) { equivalent = thisAs1SP->_isEquivalentTo(otherAs1SP.get(), criterion, dbContext); } } } return equivalent; } //! @endcond // --------------------------------------------------------------------------- std::set SingleOperation::gridsNeeded( const io::DatabaseContextPtr &databaseContext) const { std::set res; for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto &value = opParamvalue->parameterValue(); if (value->type() == ParameterValue::Type::FILENAME) { GridDescription desc; desc.shortName = value->valueFile(); if (databaseContext) { databaseContext->lookForGridInfo( desc.shortName, desc.fullName, desc.packageName, desc.url, desc.directDownload, desc.openLicense, desc.available); } res.insert(desc); } } } return res; } // --------------------------------------------------------------------------- /** \brief Validate the parameters used by a coordinate operation. * * Return whether the method is known or not, or a list of missing or extra * parameters for the operations recognized by this implementation. */ std::list SingleOperation::validateParameters() const { std::list res; const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const MethodMapping *methodMapping = nullptr; const auto methodEPSGCode = l_method->getEPSGCode(); for (const auto &mapping : projectionMethodMappings) { if (metadata::Identifier::isEquivalentName(mapping.wkt2_name, methodName.c_str()) || (methodEPSGCode != 0 && methodEPSGCode == mapping.epsg_code)) { methodMapping = &mapping; } } if (methodMapping == nullptr) { for (const auto &mapping : otherMethodMappings) { if (metadata::Identifier::isEquivalentName(mapping.wkt2_name, methodName.c_str()) || (methodEPSGCode != 0 && methodEPSGCode == mapping.epsg_code)) { methodMapping = &mapping; } } } if (!methodMapping) { res.emplace_back("Unknown method " + methodName); return res; } if (methodMapping->wkt2_name != methodName) { if (metadata::Identifier::isEquivalentName(methodMapping->wkt2_name, methodName.c_str())) { std::string msg("Method name "); msg += methodName; msg += " is equivalent to official "; msg += methodMapping->wkt2_name; msg += " but not strictly equal"; res.emplace_back(msg); } else { std::string msg("Method name "); msg += methodName; msg += ", matched to "; msg += methodMapping->wkt2_name; msg += ", through its EPSG code has not an equivalent name"; res.emplace_back(msg); } } if (methodEPSGCode != 0 && methodEPSGCode != methodMapping->epsg_code) { std::string msg("Method of EPSG code "); msg += toString(methodEPSGCode); msg += " does not match official code ("; msg += toString(methodMapping->epsg_code); msg += ')'; res.emplace_back(msg); } // Check if expected parameters are found for (int i = 0; methodMapping->params && methodMapping->params[i] != nullptr; ++i) { const auto *paramMapping = methodMapping->params[i]; const OperationParameterValue *opv = nullptr; for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if ((paramMapping->epsg_code != 0 && parameter->getEPSGCode() == paramMapping->epsg_code) || ci_equal(parameter->nameStr(), paramMapping->wkt2_name)) { opv = opParamvalue; break; } } } if (!opv) { std::string msg("Cannot find expected parameter "); msg += paramMapping->wkt2_name; res.emplace_back(msg); continue; } const auto ¶meter = opv->parameter(); if (paramMapping->wkt2_name != parameter->nameStr()) { if (ci_equal(parameter->nameStr(), paramMapping->wkt2_name)) { std::string msg("Parameter name "); msg += parameter->nameStr(); msg += " is equivalent to official "; msg += paramMapping->wkt2_name; msg += " but not strictly equal"; res.emplace_back(msg); } else { std::string msg("Parameter name "); msg += parameter->nameStr(); msg += ", matched to "; msg += paramMapping->wkt2_name; msg += ", through its EPSG code has not an equivalent name"; res.emplace_back(msg); } } const auto paramEPSGCode = parameter->getEPSGCode(); if (paramEPSGCode != 0 && paramEPSGCode != paramMapping->epsg_code) { std::string msg("Parameter of EPSG code "); msg += toString(paramEPSGCode); msg += " does not match official code ("; msg += toString(paramMapping->epsg_code); msg += ')'; res.emplace_back(msg); } } // Check if there are extra parameters for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); if (!getMapping(methodMapping, parameter)) { std::string msg("Parameter "); msg += parameter->nameStr(); msg += " found but not expected for this method"; res.emplace_back(msg); } } } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ParameterValue::Private { ParameterValue::Type type_{ParameterValue::Type::STRING}; std::unique_ptr measure_{}; std::unique_ptr stringValue_{}; int integerValue_{}; bool booleanValue_{}; explicit Private(const common::Measure &valueIn) : type_(ParameterValue::Type::MEASURE), measure_(internal::make_unique(valueIn)) {} Private(const std::string &stringValueIn, ParameterValue::Type typeIn) : type_(typeIn), stringValue_(internal::make_unique(stringValueIn)) {} explicit Private(int integerValueIn) : type_(ParameterValue::Type::INTEGER), integerValue_(integerValueIn) {} explicit Private(bool booleanValueIn) : type_(ParameterValue::Type::BOOLEAN), booleanValue_(booleanValueIn) {} }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ParameterValue::~ParameterValue() = default; //! @endcond // --------------------------------------------------------------------------- ParameterValue::ParameterValue(const common::Measure &measureIn) : d(internal::make_unique(measureIn)) {} // --------------------------------------------------------------------------- ParameterValue::ParameterValue(const std::string &stringValueIn, ParameterValue::Type typeIn) : d(internal::make_unique(stringValueIn, typeIn)) {} // --------------------------------------------------------------------------- ParameterValue::ParameterValue(int integerValueIn) : d(internal::make_unique(integerValueIn)) {} // --------------------------------------------------------------------------- ParameterValue::ParameterValue(bool booleanValueIn) : d(internal::make_unique(booleanValueIn)) {} // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a Measure (i.e. a value associated * with a * unit) * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::create(const common::Measure &measureIn) { return ParameterValue::nn_make_shared(measureIn); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a string value. * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::create(const char *stringValueIn) { return ParameterValue::nn_make_shared( std::string(stringValueIn), ParameterValue::Type::STRING); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a string value. * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::create(const std::string &stringValueIn) { return ParameterValue::nn_make_shared( stringValueIn, ParameterValue::Type::STRING); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a filename. * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::createFilename(const std::string &stringValueIn) { return ParameterValue::nn_make_shared( stringValueIn, ParameterValue::Type::FILENAME); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a integer value. * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::create(int integerValueIn) { return ParameterValue::nn_make_shared(integerValueIn); } // --------------------------------------------------------------------------- /** \brief Instantiate a ParameterValue from a boolean value. * * @return a new ParameterValue. */ ParameterValueNNPtr ParameterValue::create(bool booleanValueIn) { return ParameterValue::nn_make_shared(booleanValueIn); } // --------------------------------------------------------------------------- /** \brief Returns the type of a parameter value. * * @return the type. */ const ParameterValue::Type &ParameterValue::type() PROJ_PURE_DEFN { return d->type_; } // --------------------------------------------------------------------------- /** \brief Returns the value as a Measure (assumes type() == Type::MEASURE) * @return the value as a Measure. */ const common::Measure &ParameterValue::value() PROJ_PURE_DEFN { return *d->measure_; } // --------------------------------------------------------------------------- /** \brief Returns the value as a string (assumes type() == Type::STRING) * @return the value as a string. */ const std::string &ParameterValue::stringValue() PROJ_PURE_DEFN { return *d->stringValue_; } // --------------------------------------------------------------------------- /** \brief Returns the value as a filename (assumes type() == Type::FILENAME) * @return the value as a filename. */ const std::string &ParameterValue::valueFile() PROJ_PURE_DEFN { return *d->stringValue_; } // --------------------------------------------------------------------------- /** \brief Returns the value as a integer (assumes type() == Type::INTEGER) * @return the value as a integer. */ int ParameterValue::integerValue() PROJ_PURE_DEFN { return d->integerValue_; } // --------------------------------------------------------------------------- /** \brief Returns the value as a boolean (assumes type() == Type::BOOLEAN) * @return the value as a boolean. */ bool ParameterValue::booleanValue() PROJ_PURE_DEFN { return d->booleanValue_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const auto &l_type = type(); if (l_type == Type::MEASURE) { const auto &l_value = value(); if (formatter->abridgedTransformation()) { const auto &unit = l_value.unit(); const auto &unitType = unit.type(); if (unitType == common::UnitOfMeasure::Type::LINEAR) { formatter->add(l_value.getSIValue()); } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) { formatter->add( l_value.convertToUnit(common::UnitOfMeasure::ARC_SECOND)); } else if (unit == common::UnitOfMeasure::PARTS_PER_MILLION) { formatter->add(1.0 + l_value.value() * 1e-6); } else { formatter->add(l_value.value()); } } else { const auto &unit = l_value.unit(); if (isWKT2) { formatter->add(l_value.value()); } else { // In WKT1, as we don't output the natural unit, output to the // registered linear / angular unit. const auto &unitType = unit.type(); if (unitType == common::UnitOfMeasure::Type::LINEAR) { const auto &targetUnit = *(formatter->axisLinearUnit()); if (targetUnit.conversionToSI() == 0.0) { throw io::FormattingException( "cannot convert value to target linear unit"); } formatter->add(l_value.convertToUnit(targetUnit)); } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) { const auto &targetUnit = *(formatter->axisAngularUnit()); if (targetUnit.conversionToSI() == 0.0) { throw io::FormattingException( "cannot convert value to target angular unit"); } formatter->add(l_value.convertToUnit(targetUnit)); } else { formatter->add(l_value.getSIValue()); } } if (isWKT2 && unit != common::UnitOfMeasure::NONE) { if (!formatter ->primeMeridianOrParameterUnitOmittedIfSameAsAxis() || (unit != common::UnitOfMeasure::SCALE_UNITY && unit != *(formatter->axisLinearUnit()) && unit != *(formatter->axisAngularUnit()))) { unit._exportToWKT(formatter); } } } } else if (l_type == Type::STRING || l_type == Type::FILENAME) { formatter->addQuotedString(stringValue()); } else if (l_type == Type::INTEGER) { formatter->add(integerValue()); } else { throw io::FormattingException("boolean parameter value not handled"); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool ParameterValue::_isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &) const { auto otherPV = dynamic_cast(other); if (otherPV == nullptr) { return false; } if (type() != otherPV->type()) { return false; } switch (type()) { case Type::MEASURE: { return value()._isEquivalentTo(otherPV->value(), criterion); } case Type::STRING: case Type::FILENAME: { return stringValue() == otherPV->stringValue(); } case Type::INTEGER: { return integerValue() == otherPV->integerValue(); } case Type::BOOLEAN: { return booleanValue() == otherPV->booleanValue(); } default: { assert(false); break; } } return true; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Conversion::Private {}; //! @endcond // --------------------------------------------------------------------------- Conversion::Conversion(const OperationMethodNNPtr &methodIn, const std::vector &values) : SingleOperation(methodIn), d(nullptr) { setParameterValues(values); } // --------------------------------------------------------------------------- Conversion::Conversion(const Conversion &other) : CoordinateOperation(other), SingleOperation(other), d(nullptr) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Conversion::~Conversion() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ConversionNNPtr Conversion::shallowClone() const { auto conv = Conversion::nn_make_shared(*this); conv->assignSelf(conv); conv->setCRSs(this, false); return conv; } CoordinateOperationNNPtr Conversion::_shallowClone() const { return util::nn_static_pointer_cast(shallowClone()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ConversionNNPtr Conversion::alterParametersLinearUnit(const common::UnitOfMeasure &unit, bool convertToNewUnit) const { std::vector newValues; bool changesDone = false; for (const auto &genOpParamvalue : parameterValues()) { bool updated = false; auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶mValue = opParamvalue->parameterValue(); if (paramValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = paramValue->value(); if (measure.unit().type() == common::UnitOfMeasure::Type::LINEAR) { if (!measure.unit()._isEquivalentTo( unit, util::IComparable::Criterion::EQUIVALENT)) { const double newValue = convertToNewUnit ? measure.convertToUnit(unit) : measure.value(); newValues.emplace_back(OperationParameterValue::create( opParamvalue->parameter(), ParameterValue::create( common::Measure(newValue, unit)))); updated = true; } } } } if (updated) { changesDone = true; } else { newValues.emplace_back(genOpParamvalue); } } if (changesDone) { auto conv = create(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, "unknown"), method(), newValues); conv->setCRSs(this, false); return conv; } else { return NN_NO_CHECK( util::nn_dynamic_pointer_cast(shared_from_this())); } } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Conversion from a vector of GeneralParameterValue. * * @param properties See \ref general_properties. At minimum the name should be * defined. * @param methodIn the operation method. * @param values the values. * @return a new Conversion. * @throws InvalidOperation */ ConversionNNPtr Conversion::create(const util::PropertyMap &properties, const OperationMethodNNPtr &methodIn, const std::vector &values) // throw InvalidOperation { if (methodIn->parameters().size() != values.size()) { throw InvalidOperation( "Inconsistent number of parameters and parameter values"); } auto conv = Conversion::nn_make_shared(methodIn, values); conv->assignSelf(conv); conv->setProperties(properties); return conv; } // --------------------------------------------------------------------------- /** \brief Instantiate a Conversion and its OperationMethod * * @param propertiesConversion See \ref general_properties of the conversion. * At minimum the name should be defined. * @param propertiesOperationMethod See \ref general_properties of the operation * method. At minimum the name should be defined. * @param parameters the operation parameters. * @param values the operation values. Constraint: * values.size() == parameters.size() * @return a new Conversion. * @throws InvalidOperation */ ConversionNNPtr Conversion::create( const util::PropertyMap &propertiesConversion, const util::PropertyMap &propertiesOperationMethod, const std::vector ¶meters, const std::vector &values) // throw InvalidOperation { OperationMethodNNPtr op( OperationMethod::create(propertiesOperationMethod, parameters)); if (parameters.size() != values.size()) { throw InvalidOperation( "Inconsistent number of parameters and parameter values"); } std::vector generalParameterValues; generalParameterValues.reserve(values.size()); for (size_t i = 0; i < values.size(); i++) { generalParameterValues.push_back( OperationParameterValue::create(parameters[i], values[i])); } return create(propertiesConversion, op, generalParameterValues); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- static util::PropertyMap createMapNameEPSGCode(const std::string &name, int code) { return util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, code); } // --------------------------------------------------------------------------- static util::PropertyMap createMapNameEPSGCode(const char *name, int code) { return util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, code); } // --------------------------------------------------------------------------- static util::PropertyMap createMethodMapNameEPSGCode(int code) { const char *name = nullptr; for (const auto &tuple : methodNameCodes) { if (tuple.epsg_code == code) { name = tuple.name; break; } } assert(name); return createMapNameEPSGCode(name, code); } // --------------------------------------------------------------------------- static util::PropertyMap getUTMConversionProperty(const util::PropertyMap &properties, int zone, bool north) { if (!properties.get(common::IdentifiedObject::NAME_KEY)) { std::string conversionName("UTM zone "); conversionName += toString(zone); conversionName += (north ? 'N' : 'S'); return createMapNameEPSGCode(conversionName, (north ? 16000 : 17000) + zone); } else { return properties; } } // --------------------------------------------------------------------------- static util::PropertyMap addDefaultNameIfNeeded(const util::PropertyMap &properties, const std::string &defaultName) { if (!properties.get(common::IdentifiedObject::NAME_KEY)) { return util::PropertyMap(properties) .set(common::IdentifiedObject::NAME_KEY, defaultName); } else { return properties; } } // --------------------------------------------------------------------------- static ConversionNNPtr createConversion(const util::PropertyMap &properties, const MethodMapping *mapping, const std::vector &values) { std::vector parameters; for (int i = 0; mapping->params[i] != nullptr; i++) { const auto *param = mapping->params[i]; auto paramProperties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, param->wkt2_name); if (param->epsg_code != 0) { paramProperties .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, param->epsg_code); } auto parameter = OperationParameter::create(paramProperties); parameters.push_back(parameter); } auto methodProperties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, mapping->wkt2_name); if (mapping->epsg_code != 0) { methodProperties .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, mapping->epsg_code); } return Conversion::create( addDefaultNameIfNeeded(properties, mapping->wkt2_name), methodProperties, parameters, values); } //! @endcond // --------------------------------------------------------------------------- ConversionNNPtr Conversion::create(const util::PropertyMap &properties, int method_epsg_code, const std::vector &values) { const MethodMapping *mapping = getMapping(method_epsg_code); assert(mapping); return createConversion(properties, mapping, values); } // --------------------------------------------------------------------------- ConversionNNPtr Conversion::create(const util::PropertyMap &properties, const char *method_wkt2_name, const std::vector &values) { const MethodMapping *mapping = getMapping(method_wkt2_name); assert(mapping); return createConversion(properties, mapping, values); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct VectorOfParameters : public std::vector { VectorOfParameters() : std::vector() {} explicit VectorOfParameters( std::initializer_list list) : std::vector(list) {} VectorOfParameters(const VectorOfParameters &) = delete; ~VectorOfParameters(); }; // This way, we disable inlining of destruction, and save a lot of space VectorOfParameters::~VectorOfParameters() = default; struct VectorOfValues : public std::vector { VectorOfValues() : std::vector() {} explicit VectorOfValues(std::initializer_list list) : std::vector(list) {} explicit VectorOfValues(std::initializer_list list); VectorOfValues(const VectorOfValues &) = delete; VectorOfValues(VectorOfValues &&) = default; ~VectorOfValues(); }; static std::vector buildParameterValueFromMeasure( const std::initializer_list &list) { std::vector res; for (const auto &v : list) { res.emplace_back(ParameterValue::create(v)); } return res; } VectorOfValues::VectorOfValues(std::initializer_list list) : std::vector(buildParameterValueFromMeasure(list)) {} // This way, we disable inlining of destruction, and save a lot of space VectorOfValues::~VectorOfValues() = default; PROJ_NO_INLINE static VectorOfValues createParams(const common::Measure &m1, const common::Measure &m2, const common::Measure &m3) { return VectorOfValues{ParameterValue::create(m1), ParameterValue::create(m2), ParameterValue::create(m3)}; } PROJ_NO_INLINE static VectorOfValues createParams(const common::Measure &m1, const common::Measure &m2, const common::Measure &m3, const common::Measure &m4) { return VectorOfValues{ ParameterValue::create(m1), ParameterValue::create(m2), ParameterValue::create(m3), ParameterValue::create(m4)}; } PROJ_NO_INLINE static VectorOfValues createParams(const common::Measure &m1, const common::Measure &m2, const common::Measure &m3, const common::Measure &m4, const common::Measure &m5) { return VectorOfValues{ ParameterValue::create(m1), ParameterValue::create(m2), ParameterValue::create(m3), ParameterValue::create(m4), ParameterValue::create(m5), }; } PROJ_NO_INLINE static VectorOfValues createParams(const common::Measure &m1, const common::Measure &m2, const common::Measure &m3, const common::Measure &m4, const common::Measure &m5, const common::Measure &m6) { return VectorOfValues{ ParameterValue::create(m1), ParameterValue::create(m2), ParameterValue::create(m3), ParameterValue::create(m4), ParameterValue::create(m5), ParameterValue::create(m6), }; } PROJ_NO_INLINE static VectorOfValues createParams(const common::Measure &m1, const common::Measure &m2, const common::Measure &m3, const common::Measure &m4, const common::Measure &m5, const common::Measure &m6, const common::Measure &m7) { return VectorOfValues{ ParameterValue::create(m1), ParameterValue::create(m2), ParameterValue::create(m3), ParameterValue::create(m4), ParameterValue::create(m5), ParameterValue::create(m6), ParameterValue::create(m7), }; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a [Universal Transverse Mercator] *(https://proj.org/operations/projections/utm.html) conversion. * * UTM is a family of conversions, of EPSG codes from 16001 to 16060 for the * northern hemisphere, and 17001 to 17060 for the southern hemisphere, * based on the Transverse Mercator projection method. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param zone UTM zone number between 1 and 60. * @param north true for UTM northern hemisphere, false for UTM southern * hemisphere. * @return a new Conversion. */ ConversionNNPtr Conversion::createUTM(const util::PropertyMap &properties, int zone, bool north) { return create( getUTMConversionProperty(properties, zone, north), EPSG_CODE_METHOD_TRANSVERSE_MERCATOR, createParams(common::Angle(UTM_LATITUDE_OF_NATURAL_ORIGIN), common::Angle(zone * 6.0 - 183.0), common::Scale(UTM_SCALE_FACTOR), common::Length(UTM_FALSE_EASTING), common::Length(north ? UTM_NORTH_FALSE_NORTHING : UTM_SOUTH_FALSE_NORTHING))); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Transverse Mercator] *(https://proj.org/operations/projections/tmerc.html) projection method. * * This method is defined as [EPSG:9807] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9807) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createTransverseMercator( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_TRANSVERSE_MERCATOR, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Gauss Schreiber Transverse *Mercator] *(https://proj.org/operations/projections/gstmerc.html) projection method. * * This method is also known as Gauss-Laborde Reunion. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGaussSchreiberTransverseMercator( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_GAUSS_SCHREIBER_TRANSVERSE_MERCATOR, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Transverse Mercator South *Orientated] *(https://proj.org/operations/projections/tmerc.html) projection method. * * This method is defined as [EPSG:9808] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9808) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createTransverseMercatorSouthOriented( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_TRANSVERSE_MERCATOR_SOUTH_ORIENTATED, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Two Point Equidistant] *(https://proj.org/operations/projections/tpeqd.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstPoint Latitude of first point. * @param longitudeFirstPoint Longitude of first point. * @param latitudeSecondPoint Latitude of second point. * @param longitudeSeconPoint Longitude of second point. * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createTwoPointEquidistant(const util::PropertyMap &properties, const common::Angle &latitudeFirstPoint, const common::Angle &longitudeFirstPoint, const common::Angle &latitudeSecondPoint, const common::Angle &longitudeSeconPoint, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_TWO_POINT_EQUIDISTANT, createParams(latitudeFirstPoint, longitudeFirstPoint, latitudeSecondPoint, longitudeSeconPoint, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Tunisia Mapping Grid projection * method. * * This method is defined as [EPSG:9816] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9816) * * \note There is currently no implementation of the method formulas in PROJ. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createTunisiaMappingGrid( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_TUNISIA_MAPPING_GRID, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Albers Conic Equal Area] *(https://proj.org/operations/projections/aea.html) projection method. * * This method is defined as [EPSG:9822] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9822) * * @note the order of arguments is conformant with the corresponding EPSG * mode and different than OGRSpatialReference::setACEA() of GDAL <= 2.3 * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFalseOrigin See \ref latitude_false_origin * @param longitudeFalseOrigin See \ref longitude_false_origin * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param eastingFalseOrigin See \ref easting_false_origin * @param northingFalseOrigin See \ref northing_false_origin * @return a new Conversion. */ ConversionNNPtr Conversion::createAlbersEqualArea(const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, const common::Angle &longitudeFalseOrigin, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &eastingFalseOrigin, const common::Length &northingFalseOrigin) { return create(properties, EPSG_CODE_METHOD_ALBERS_EQUAL_AREA, createParams(latitudeFalseOrigin, longitudeFalseOrigin, latitudeFirstParallel, latitudeSecondParallel, eastingFalseOrigin, northingFalseOrigin)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Conic Conformal 1SP] *(https://proj.org/operations/projections/lcc.html) projection method. * * This method is defined as [EPSG:9801] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9801) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertConicConformal_1SP( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Conic Conformal (2SP)] *(https://proj.org/operations/projections/lcc.html) projection method. * * This method is defined as [EPSG:9802] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9802) * * @note the order of arguments is conformant with the corresponding EPSG * mode and different than OGRSpatialReference::setLCC() of GDAL <= 2.3 * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFalseOrigin See \ref latitude_false_origin * @param longitudeFalseOrigin See \ref longitude_false_origin * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param eastingFalseOrigin See \ref easting_false_origin * @param northingFalseOrigin See \ref northing_false_origin * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertConicConformal_2SP( const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, const common::Angle &longitudeFalseOrigin, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &eastingFalseOrigin, const common::Length &northingFalseOrigin) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP, createParams(latitudeFalseOrigin, longitudeFalseOrigin, latitudeFirstParallel, latitudeSecondParallel, eastingFalseOrigin, northingFalseOrigin)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Conic Conformal (2SP *Michigan)] *(https://proj.org/operations/projections/lcc.html) projection method. * * This method is defined as [EPSG:1051] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1051) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFalseOrigin See \ref latitude_false_origin * @param longitudeFalseOrigin See \ref longitude_false_origin * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param eastingFalseOrigin See \ref easting_false_origin * @param northingFalseOrigin See \ref northing_false_origin * @param ellipsoidScalingFactor Ellipsoid scaling factor. * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertConicConformal_2SP_Michigan( const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, const common::Angle &longitudeFalseOrigin, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &eastingFalseOrigin, const common::Length &northingFalseOrigin, const common::Scale &ellipsoidScalingFactor) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN, createParams(latitudeFalseOrigin, longitudeFalseOrigin, latitudeFirstParallel, latitudeSecondParallel, eastingFalseOrigin, northingFalseOrigin, ellipsoidScalingFactor)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Conic Conformal (2SP *Belgium)] *(https://proj.org/operations/projections/lcc.html) projection method. * * This method is defined as [EPSG:9803] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9803) * * \warning The formulas used currently in PROJ are, incorrectly, the ones of * the regular LCC_2SP method. * * @note the order of arguments is conformant with the corresponding EPSG * mode and different than OGRSpatialReference::setLCCB() of GDAL <= 2.3 * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFalseOrigin See \ref latitude_false_origin * @param longitudeFalseOrigin See \ref longitude_false_origin * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param eastingFalseOrigin See \ref easting_false_origin * @param northingFalseOrigin See \ref northing_false_origin * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertConicConformal_2SP_Belgium( const util::PropertyMap &properties, const common::Angle &latitudeFalseOrigin, const common::Angle &longitudeFalseOrigin, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &eastingFalseOrigin, const common::Length &northingFalseOrigin) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_BELGIUM, createParams(latitudeFalseOrigin, longitudeFalseOrigin, latitudeFirstParallel, latitudeSecondParallel, eastingFalseOrigin, northingFalseOrigin)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Modified Azimuthal *Equidistant] *(https://proj.org/operations/projections/aeqd.html) projection method. * * This method is defined as [EPSG:9832] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9832) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeNatOrigin See \ref center_latitude * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createAzimuthalEquidistant( const util::PropertyMap &properties, const common::Angle &latitudeNatOrigin, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_MODIFIED_AZIMUTHAL_EQUIDISTANT, createParams(latitudeNatOrigin, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Guam Projection] *(https://proj.org/operations/projections/aeqd.html) projection method. * * This method is defined as [EPSG:9831] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9831) * * @param properties See \ref general_properties of the conversion. If the name *is * not provided, it is automatically set. * @param latitudeNatOrigin See \ref center_latitude * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGuamProjection( const util::PropertyMap &properties, const common::Angle &latitudeNatOrigin, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_GUAM_PROJECTION, createParams(latitudeNatOrigin, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Bonne] *(https://proj.org/operations/projections/bonne.html) projection method. * * This method is defined as [EPSG:9827] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9827) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeNatOrigin See \ref center_latitude . PROJ calls its the * standard parallel 1. * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createBonne(const util::PropertyMap &properties, const common::Angle &latitudeNatOrigin, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_BONNE, createParams(latitudeNatOrigin, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Cylindrical Equal Area *(Spherical)] *(https://proj.org/operations/projections/cea.html) projection method. * * This method is defined as [EPSG:9834] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9834) * * \warning The PROJ cea computation code would select the ellipsoidal form if * a non-spherical ellipsoid is used for the base GeographicCRS. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstParallel See \ref latitude_first_std_parallel. * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertCylindricalEqualAreaSpherical( const util::PropertyMap &properties, const common::Angle &latitudeFirstParallel, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA_SPHERICAL, createParams(latitudeFirstParallel, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Cylindrical Equal Area *(ellipsoidal form)] *(https://proj.org/operations/projections/cea.html) projection method. * * This method is defined as [EPSG:9835] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9835) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstParallel See \ref latitude_first_std_parallel. * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertCylindricalEqualArea( const util::PropertyMap &properties, const common::Angle &latitudeFirstParallel, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_LAMBERT_CYLINDRICAL_EQUAL_AREA, createParams(latitudeFirstParallel, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Cassini-Soldner] * (https://proj.org/operations/projections/cass.html) projection method. * * This method is defined as [EPSG:9806] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9806) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createCassiniSoldner( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_CASSINI_SOLDNER, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Equidistant Conic] *(https://proj.org/operations/projections/eqdc.html) projection method. * * There is no equivalent in EPSG. * * @note Although not found in EPSG, the order of arguments is conformant with * the "spirit" of EPSG and different than OGRSpatialReference::setEC() of GDAL *<= 2.3 * @param properties See \ref general_properties of the conversion. *If the name * is not provided, it is automatically set. * * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEquidistantConic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_EQUIDISTANT_CONIC, createParams(centerLat, centerLong, latitudeFirstParallel, latitudeSecondParallel, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert I] * (https://proj.org/operations/projections/eck1.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertI(const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_I, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert II] * (https://proj.org/operations/projections/eck2.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertII( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_II, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert III] * (https://proj.org/operations/projections/eck3.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertIII( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_III, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert IV] * (https://proj.org/operations/projections/eck4.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertIV( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_IV, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert V] * (https://proj.org/operations/projections/eck5.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertV(const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_V, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Eckert VI] * (https://proj.org/operations/projections/eck6.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEckertVI( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ECKERT_VI, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Equidistant Cylindrical] *(https://proj.org/operations/projections/eqc.html) projection method. * * This is also known as the Equirectangular method, and in the particular case * where the latitude of first parallel is 0. * * This method is defined as [EPSG:1028] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1028) * * @note This is the equivalent OGRSpatialReference::SetEquirectangular2( * 0.0, latitudeFirstParallel, falseEasting, falseNorthing ) of GDAL <= 2.3, * where the lat_0 / center_latitude parameter is forced to 0. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstParallel See \ref latitude_first_std_parallel. * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEquidistantCylindrical( const util::PropertyMap &properties, const common::Angle &latitudeFirstParallel, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL, createParams(latitudeFirstParallel, 0.0, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Equidistant Cylindrical *(Spherical)] *(https://proj.org/operations/projections/eqc.html) projection method. * * This is also known as the Equirectangular method, and in the particular case * where the latitude of first parallel is 0. * * This method is defined as [EPSG:1029] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1029) * * @note This is the equivalent OGRSpatialReference::SetEquirectangular2( * 0.0, latitudeFirstParallel, falseEasting, falseNorthing ) of GDAL <= 2.3, * where the lat_0 / center_latitude parameter is forced to 0. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstParallel See \ref latitude_first_std_parallel. * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEquidistantCylindricalSpherical( const util::PropertyMap &properties, const common::Angle &latitudeFirstParallel, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL, createParams(latitudeFirstParallel, 0.0, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Gall (Stereographic)] * (https://proj.org/operations/projections/gall.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGall(const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_GALL_STEREOGRAPHIC, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Goode Homolosine] * (https://proj.org/operations/projections/goode.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGoodeHomolosine( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_GOODE_HOMOLOSINE, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Interrupted Goode Homolosine] * (https://proj.org/operations/projections/igh.html) projection method. * * There is no equivalent in EPSG. * * @note OGRSpatialReference::SetIGH() of GDAL <= 2.3 assumes the 3 * projection * parameters to be zero and this is the nominal case. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createInterruptedGoodeHomolosine( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_INTERRUPTED_GOODE_HOMOLOSINE, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Geostationary Satellite View] * (https://proj.org/operations/projections/geos.html) projection method, * with the sweep angle axis of the viewing instrument being x * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param height Height of the view point above the Earth. * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGeostationarySatelliteSweepX( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &height, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X, createParams(centerLong, height, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Geostationary Satellite View] * (https://proj.org/operations/projections/geos.html) projection method, * with the sweep angle axis of the viewing instrument being y. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param height Height of the view point above the Earth. * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGeostationarySatelliteSweepY( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &height, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_Y, createParams(centerLong, height, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Gnomonic] *(https://proj.org/operations/projections/gnom.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createGnomonic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, PROJ_WKT2_NAME_METHOD_GNOMONIC, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Hotine Oblique Mercator *(Variant A)] *(https://proj.org/operations/projections/omerc.html) projection method * * This is the variant with the no_uoff parameter, which corresponds to * GDAL >=2.3 Hotine_Oblique_Mercator projection. * In this variant, the false grid coordinates are * defined at the intersection of the initial line and the aposphere (the * equator on one of the intermediate surfaces inherent in the method), that is * at the natural origin of the coordinate system). * * This method is defined as [EPSG:9812] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9812) * * \note In the case where azimuthInitialLine = angleFromRectifiedToSkrewGrid = *90deg, * this maps to the [Swiss Oblique Mercator] *(https://proj.org/operations/projections/somerc.html) formulas. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param longitudeProjectionCentre See \ref longitude_projection_centre * @param azimuthInitialLine See \ref azimuth_initial_line * @param angleFromRectifiedToSkrewGrid See * \ref angle_from_recitfied_to_skrew_grid * @param scale See \ref scale_factor_initial_line * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createHotineObliqueMercatorVariantA( const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &longitudeProjectionCentre, const common::Angle &azimuthInitialLine, const common::Angle &angleFromRectifiedToSkrewGrid, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A, createParams(latitudeProjectionCentre, longitudeProjectionCentre, azimuthInitialLine, angleFromRectifiedToSkrewGrid, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Hotine Oblique Mercator *(Variant B)] *(https://proj.org/operations/projections/omerc.html) projection method * * This is the variant without the no_uoff parameter, which corresponds to * GDAL >=2.3 Hotine_Oblique_Mercator_Azimuth_Center projection. * In this variant, the false grid coordinates are defined at the projection *centre. * * This method is defined as [EPSG:9815] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9815) * * \note In the case where azimuthInitialLine = angleFromRectifiedToSkrewGrid = *90deg, * this maps to the [Swiss Oblique Mercator] *(https://proj.org/operations/projections/somerc.html) formulas. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param longitudeProjectionCentre See \ref longitude_projection_centre * @param azimuthInitialLine See \ref azimuth_initial_line * @param angleFromRectifiedToSkrewGrid See * \ref angle_from_recitfied_to_skrew_grid * @param scale See \ref scale_factor_initial_line * @param eastingProjectionCentre See \ref easting_projection_centre * @param northingProjectionCentre See \ref northing_projection_centre * @return a new Conversion. */ ConversionNNPtr Conversion::createHotineObliqueMercatorVariantB( const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &longitudeProjectionCentre, const common::Angle &azimuthInitialLine, const common::Angle &angleFromRectifiedToSkrewGrid, const common::Scale &scale, const common::Length &eastingProjectionCentre, const common::Length &northingProjectionCentre) { return create( properties, EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B, createParams(latitudeProjectionCentre, longitudeProjectionCentre, azimuthInitialLine, angleFromRectifiedToSkrewGrid, scale, eastingProjectionCentre, northingProjectionCentre)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Hotine Oblique Mercator Two *Point Natural Origin] *(https://proj.org/operations/projections/omerc.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param latitudePoint1 Latitude of point 1. * @param longitudePoint1 Latitude of point 1. * @param latitudePoint2 Latitude of point 2. * @param longitudePoint2 Longitude of point 2. * @param scale See \ref scale_factor_initial_line * @param eastingProjectionCentre See \ref easting_projection_centre * @param northingProjectionCentre See \ref northing_projection_centre * @return a new Conversion. */ ConversionNNPtr Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin( const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &latitudePoint1, const common::Angle &longitudePoint1, const common::Angle &latitudePoint2, const common::Angle &longitudePoint2, const common::Scale &scale, const common::Length &eastingProjectionCentre, const common::Length &northingProjectionCentre) { return create( properties, PROJ_WKT2_NAME_METHOD_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN, { ParameterValue::create(latitudeProjectionCentre), ParameterValue::create(latitudePoint1), ParameterValue::create(longitudePoint1), ParameterValue::create(latitudePoint2), ParameterValue::create(longitudePoint2), ParameterValue::create(scale), ParameterValue::create(eastingProjectionCentre), ParameterValue::create(northingProjectionCentre), }); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Laborde Oblique Mercator] *(https://proj.org/operations/projections/labrd.html) projection method. * * This method is defined as [EPSG:9813] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9813) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param longitudeProjectionCentre See \ref longitude_projection_centre * @param azimuthInitialLine See \ref azimuth_initial_line * @param scale See \ref scale_factor_initial_line * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createLabordeObliqueMercator( const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &longitudeProjectionCentre, const common::Angle &azimuthInitialLine, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_LABORDE_OBLIQUE_MERCATOR, createParams(latitudeProjectionCentre, longitudeProjectionCentre, azimuthInitialLine, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [International Map of the World *Polyconic] *(https://proj.org/operations/projections/imw_p.html) projection method. * * There is no equivalent in EPSG. * * @note the order of arguments is conformant with the corresponding EPSG * mode and different than OGRSpatialReference::SetIWMPolyconic() of GDAL <= *2.3 * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param latitudeSecondParallel See \ref latitude_second_std_parallel * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createInternationalMapWorldPolyconic( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Angle &latitudeFirstParallel, const common::Angle &latitudeSecondParallel, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_INTERNATIONAL_MAP_WORLD_POLYCONIC, createParams(centerLong, latitudeFirstParallel, latitudeSecondParallel, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Krovak (north oriented)] *(https://proj.org/operations/projections/krovak.html) projection method. * * This method is defined as [EPSG:1041] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1041) * * The coordinates are returned in the "GIS friendly" order: easting, northing. * This method is similar to createKrovak(), except that the later returns * projected values as southing, westing, where * southing(Krovak) = -northing(Krovak_North) and * westing(Krovak) = -easting(Krovak_North). * * @note The current PROJ implementation of Krovak hard-codes * colatitudeConeAxis = 30deg17'17.30311" * and latitudePseudoStandardParallel = 78deg30'N, which are the values used for * the ProjectedCRS S-JTSK (Ferro) / Krovak East North (EPSG:5221). * It also hard-codes the parameters of the Bessel ellipsoid typically used for * Krovak. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param longitudeOfOrigin See \ref longitude_of_origin * @param colatitudeConeAxis See \ref colatitude_cone_axis * @param latitudePseudoStandardParallel See \ref *latitude_pseudo_standard_parallel * @param scaleFactorPseudoStandardParallel See \ref *scale_factor_pseudo_standard_parallel * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createKrovakNorthOriented( const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &longitudeOfOrigin, const common::Angle &colatitudeConeAxis, const common::Angle &latitudePseudoStandardParallel, const common::Scale &scaleFactorPseudoStandardParallel, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_KROVAK_NORTH_ORIENTED, createParams(latitudeProjectionCentre, longitudeOfOrigin, colatitudeConeAxis, latitudePseudoStandardParallel, scaleFactorPseudoStandardParallel, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Krovak] *(https://proj.org/operations/projections/krovak.html) projection method. * * This method is defined as [EPSG:9819] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9819) * * The coordinates are returned in the historical order: southing, westing * This method is similar to createKrovakNorthOriented(), except that the later *returns * projected values as easting, northing, where * easting(Krovak_North) = -westing(Krovak) and * northing(Krovak_North) = -southing(Krovak). * * @note The current PROJ implementation of Krovak hard-codes * colatitudeConeAxis = 30deg17'17.30311" * and latitudePseudoStandardParallel = 78deg30'N, which are the values used for * the ProjectedCRS S-JTSK (Ferro) / Krovak East North (EPSG:5221). * It also hard-codes the parameters of the Bessel ellipsoid typically used for * Krovak. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeProjectionCentre See \ref latitude_projection_centre * @param longitudeOfOrigin See \ref longitude_of_origin * @param colatitudeConeAxis See \ref colatitude_cone_axis * @param latitudePseudoStandardParallel See \ref *latitude_pseudo_standard_parallel * @param scaleFactorPseudoStandardParallel See \ref *scale_factor_pseudo_standard_parallel * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createKrovak(const util::PropertyMap &properties, const common::Angle &latitudeProjectionCentre, const common::Angle &longitudeOfOrigin, const common::Angle &colatitudeConeAxis, const common::Angle &latitudePseudoStandardParallel, const common::Scale &scaleFactorPseudoStandardParallel, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_KROVAK, createParams(latitudeProjectionCentre, longitudeOfOrigin, colatitudeConeAxis, latitudePseudoStandardParallel, scaleFactorPseudoStandardParallel, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Lambert Azimuthal Equal Area] *(https://proj.org/operations/projections/laea.html) projection method. * * This method is defined as [EPSG:9820] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9820) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeNatOrigin See \ref center_latitude * @param longitudeNatOrigin See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createLambertAzimuthalEqualArea( const util::PropertyMap &properties, const common::Angle &latitudeNatOrigin, const common::Angle &longitudeNatOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_LAMBERT_AZIMUTHAL_EQUAL_AREA, createParams(latitudeNatOrigin, longitudeNatOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Miller Cylindrical] *(https://proj.org/operations/projections/mill.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createMillerCylindrical( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_MILLER_CYLINDRICAL, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Mercator] *(https://proj.org/operations/projections/merc.html) projection method. * * This is the variant, also known as Mercator (1SP), defined with the scale * factor. Note that latitude of natural origin (centerLat) is a parameter, * but unused in the transformation formulas. * * This method is defined as [EPSG:9804] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9804) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude . Should be 0. * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createMercatorVariantA( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_MERCATOR_VARIANT_A, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Mercator] *(https://proj.org/operations/projections/merc.html) projection method. * * This is the variant, also known as Mercator (2SP), defined with the latitude * of the first standard parallel (the second standard parallel is implicitly * the opposite value). The latitude of natural origin is fixed to zero. * * This method is defined as [EPSG:9805] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9805) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeFirstParallel See \ref latitude_first_std_parallel * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createMercatorVariantB( const util::PropertyMap &properties, const common::Angle &latitudeFirstParallel, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_MERCATOR_VARIANT_B, createParams(latitudeFirstParallel, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Popular Visualisation Pseudo *Mercator] *(https://proj.org/operations/projections/webmerc.html) projection method. * * Also known as WebMercator. Mostly/only used for Projected CRS EPSG:3857 * (WGS 84 / Pseudo-Mercator) * * This method is defined as [EPSG:1024] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1024) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude . Usually 0 * @param centerLong See \ref center_longitude . Usually 0 * @param falseEasting See \ref false_easting . Usually 0 * @param falseNorthing See \ref false_northing . Usually 0 * @return a new Conversion. */ ConversionNNPtr Conversion::createPopularVisualisationPseudoMercator( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Mollweide] * (https://proj.org/operations/projections/moll.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createMollweide( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_MOLLWEIDE, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [New Zealand Map Grid] * (https://proj.org/operations/projections/nzmg.html) projection method. * * This method is defined as [EPSG:9811] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9811) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createNewZealandMappingGrid( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_NZMG, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Oblique Stereographic *(Alternative)] *(https://proj.org/operations/projections/sterea.html) projection method. * * This method is defined as [EPSG:9809] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9809) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createObliqueStereographic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_OBLIQUE_STEREOGRAPHIC, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Orthographic] *(https://proj.org/operations/projections/ortho.html) projection method. * * This method is defined as [EPSG:9840] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9840) * * \note At the time of writing, PROJ only implements the spherical formulation * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createOrthographic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_ORTHOGRAPHIC, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [American Polyconic] *(https://proj.org/operations/projections/poly.html) projection method. * * This method is defined as [EPSG:9818] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9818) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createAmericanPolyconic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, EPSG_CODE_METHOD_AMERICAN_POLYCONIC, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Polar Stereographic (Variant *A)] *(https://proj.org/operations/projections/stere.html) projection method. * * This method is defined as [EPSG:9810] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9810) * * This is the variant of polar stereographic defined with a scale factor. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude . Should be 90 deg ou -90 deg. * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createPolarStereographicVariantA( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_A, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Polar Stereographic (Variant *B)] *(https://proj.org/operations/projections/stere.html) projection method. * * This method is defined as [EPSG:9829] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9829) * * This is the variant of polar stereographic defined with a latitude of * standard parallel. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeStandardParallel See \ref latitude_std_parallel * @param longitudeOfOrigin See \ref longitude_of_origin * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createPolarStereographicVariantB( const util::PropertyMap &properties, const common::Angle &latitudeStandardParallel, const common::Angle &longitudeOfOrigin, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B, createParams(latitudeStandardParallel, longitudeOfOrigin, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Robinson] * (https://proj.org/operations/projections/robin.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createRobinson( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_ROBINSON, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Sinusoidal] * (https://proj.org/operations/projections/sinu.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createSinusoidal( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_SINUSOIDAL, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Stereographic] *(https://proj.org/operations/projections/stere.html) projection method. * * There is no equivalent in EPSG. This method implements the original "Oblique * Stereographic" method described in "Snyder's Map Projections - A Working *manual", * which is different from the "Oblique Stereographic (alternative") method * implemented in createObliqueStereographic(). * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param scale See \ref scale * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createStereographic( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Scale &scale, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_STEREOGRAPHIC, createParams(centerLat, centerLong, scale, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Van der Grinten] * (https://proj.org/operations/projections/vandg.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createVanDerGrinten( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_VAN_DER_GRINTEN, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner I] * (https://proj.org/operations/projections/wag1.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerI(const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_I, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner II] * (https://proj.org/operations/projections/wag2.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerII( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_II, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner III] * (https://proj.org/operations/projections/wag3.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param latitudeTrueScale Latitude of true scale. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerIII( const util::PropertyMap &properties, const common::Angle &latitudeTrueScale, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_III, createParams(latitudeTrueScale, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner IV] * (https://proj.org/operations/projections/wag4.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerIV( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_IV, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner V] * (https://proj.org/operations/projections/wag5.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerV(const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_V, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner VI] * (https://proj.org/operations/projections/wag6.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerVI( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_VI, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Wagner VII] * (https://proj.org/operations/projections/wag7.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createWagnerVII( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, PROJ_WKT2_NAME_METHOD_WAGNER_VII, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Quadrilateralized Spherical *Cube] *(https://proj.org/operations/projections/qsc.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLat See \ref center_latitude * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createQuadrilateralizedSphericalCube( const util::PropertyMap &properties, const common::Angle ¢erLat, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create( properties, PROJ_WKT2_NAME_METHOD_QUADRILATERALIZED_SPHERICAL_CUBE, createParams(centerLat, centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Spherical Cross-Track Height] *(https://proj.org/operations/projections/sch.html) projection method. * * There is no equivalent in EPSG. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param pegPointLat Peg point latitude. * @param pegPointLong Peg point longitude. * @param pegPointHeading Peg point heading. * @param pegPointHeight Peg point height. * @return a new Conversion. */ ConversionNNPtr Conversion::createSphericalCrossTrackHeight( const util::PropertyMap &properties, const common::Angle &pegPointLat, const common::Angle &pegPointLong, const common::Angle &pegPointHeading, const common::Length &pegPointHeight) { return create(properties, PROJ_WKT2_NAME_METHOD_SPHERICAL_CROSS_TRACK_HEIGHT, createParams(pegPointLat, pegPointLong, pegPointHeading, pegPointHeight)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Equal Earth] * (https://proj.org/operations/projections/eqearth.html) projection method. * * This method is defined as [EPSG:1078] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1078) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param centerLong See \ref center_longitude * @param falseEasting See \ref false_easting * @param falseNorthing See \ref false_northing * @return a new Conversion. */ ConversionNNPtr Conversion::createEqualEarth( const util::PropertyMap &properties, const common::Angle ¢erLong, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_EQUAL_EARTH, createParams(centerLong, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the [Vertical Perspective] * (https://proj.org/operations/projections/nsper.html) projection method. * * This method is defined as [EPSG:9838] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9838) * * The PROJ implementation of the EPSG Vertical Perspective has the current * limitations with respect to the method described in EPSG: *
    *
  • it is a 2D-only method, ignoring the ellipsoidal height of the point to * project.
  • *
  • it has only a spherical development.
  • *
  • the height of the topocentric origin is ignored, and thus assumed to be * 0.
  • *
* * For completness, PROJ adds the falseEasting and falseNorthing parameter, * which are not described in EPSG. They should usually be set to 0. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param topoOriginLat Latitude of topocentric origin * @param topoOriginLong Longitude of topocentric origin * @param topoOriginHeight Ellipsoidal height of topocentric origin. Ignored by * PROJ (that is assumed to be 0) * @param viewPointHeight Viewpoint height with respect to the * topocentric/mapping plane. In the case where topoOriginHeight = 0, this is * the height above the ellipsoid surface at topoOriginLat, topoOriginLong. * @param falseEasting See \ref false_easting . (not in EPSG) * @param falseNorthing See \ref false_northing . (not in EPSG) * @return a new Conversion. * * @since 6.3 */ ConversionNNPtr Conversion::createVerticalPerspective( const util::PropertyMap &properties, const common::Angle &topoOriginLat, const common::Angle &topoOriginLong, const common::Length &topoOriginHeight, const common::Length &viewPointHeight, const common::Length &falseEasting, const common::Length &falseNorthing) { return create(properties, EPSG_CODE_METHOD_VERTICAL_PERSPECTIVE, createParams(topoOriginLat, topoOriginLong, topoOriginHeight, viewPointHeight, falseEasting, falseNorthing)); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Pole Rotation method, using * the conventions of the GRIB 1 and GRIB 2 data formats. * * Those are mentionned in the Note 2 of * https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp3-1.shtml * * Several conventions for the pole rotation method exists. * The parameters provided in this method are remapped to the PROJ ob_tran * operation with: *
 * +proj=ob_tran +o_proj=longlat +o_lon_p=-rotationAngle
 *                               +o_lat_p=-southPoleLatInUnrotatedCRS
 *                               +lon_0=southPoleLongInUnrotatedCRS
 * 
* * Another implementation of that convention is also in the netcdf-java library: * https://github.com/Unidata/netcdf-java/blob/3ce72c0cd167609ed8c69152bb4a004d1daa9273/cdm/core/src/main/java/ucar/unidata/geoloc/projection/RotatedLatLon.java * * The PROJ implementation of this method assumes a spherical ellipsoid. * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param southPoleLatInUnrotatedCRS Latitude of the point from the unrotated * CRS, expressed in the unrotated CRS, that will become the south pole of the * rotated CRS. * @param southPoleLongInUnrotatedCRS Longitude of the point from the unrotated * CRS, expressed in the unrotated CRS, that will become the south pole of the * rotated CRS. * @param axisRotation The angle of rotation about the new polar * axis (measured clockwise when looking from the southern to the northern pole) * of the coordinate system, assuming the new axis to have been obtained by * first rotating the sphere through southPoleLongInUnrotatedCRS degrees about * the geographic polar axis and then rotating through * (90 + southPoleLatInUnrotatedCRS) degrees so that the southern pole moved * along the (previously rotated) Greenwich meridian. * @return a new Conversion. * * @since 7.0 */ ConversionNNPtr Conversion::createPoleRotationGRIBConvention( const util::PropertyMap &properties, const common::Angle &southPoleLatInUnrotatedCRS, const common::Angle &southPoleLongInUnrotatedCRS, const common::Angle &axisRotation) { return create(properties, PROJ_WKT2_NAME_METHOD_POLE_ROTATION_GRIB_CONVENTION, createParams(southPoleLatInUnrotatedCRS, southPoleLongInUnrotatedCRS, axisRotation)); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static OperationParameterNNPtr createOpParamNameEPSGCode(int code) { const char *name = OperationParameter::getNameForEPSGCode(code); assert(name); return OperationParameter::create(createMapNameEPSGCode(name, code)); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Change of Vertical Unit * method. * * This method is defined as [EPSG:1069] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1069) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param factor Conversion factor * @return a new Conversion. */ ConversionNNPtr Conversion::createChangeVerticalUnit(const util::PropertyMap &properties, const common::Scale &factor) { return create(properties, createMethodMapNameEPSGCode( EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT), VectorOfParameters{ createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR), }, VectorOfValues{ factor, }); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Height Depth Reversal * method. * * This method is defined as [EPSG:1068] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1068) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @return a new Conversion. * @since 6.3 */ ConversionNNPtr Conversion::createHeightDepthReversal(const util::PropertyMap &properties) { return create(properties, createMethodMapNameEPSGCode( EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL), {}, {}); } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Axis order reversal method * * This swaps the longitude, latitude axis. * * This method is defined as [EPSG:9843] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9843), * or for 3D as [EPSG:9844] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9844) * * @param is3D Whether this should apply on 3D geographicCRS * @return a new Conversion. */ ConversionNNPtr Conversion::createAxisOrderReversal(bool is3D) { if (is3D) { return create(createMapNameEPSGCode( "axis order change (geographic3D horizontal)", 15499), createMethodMapNameEPSGCode( EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_3D), {}, {}); } else { return create(createMapNameEPSGCode(AXIS_ORDER_CHANGE_2D_NAME, 15498), createMethodMapNameEPSGCode( EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_2D), {}, {}); } } // --------------------------------------------------------------------------- /** \brief Instantiate a conversion based on the Geographic/Geocentric method. * * This method is defined as [EPSG:9602] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9602), * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @return a new Conversion. */ ConversionNNPtr Conversion::createGeographicGeocentric(const util::PropertyMap &properties) { return create(properties, createMethodMapNameEPSGCode( EPSG_CODE_METHOD_GEOGRAPHIC_GEOCENTRIC), {}, {}); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const char *getCRSQualifierStr(const crs::CRSPtr &crs) { auto geod = dynamic_cast(crs.get()); if (geod) { if (geod->isGeocentric()) { return " (geocentric)"; } auto geog = dynamic_cast(geod); if (geog) { if (geog->coordinateSystem()->axisList().size() == 2) { return " (geog2D)"; } else { return " (geog3D)"; } } } return ""; } // --------------------------------------------------------------------------- static std::string buildOpName(const char *opType, const crs::CRSPtr &source, const crs::CRSPtr &target) { std::string res(opType); const auto &srcName = source->nameStr(); const auto &targetName = target->nameStr(); const char *srcQualifier = ""; const char *targetQualifier = ""; if (srcName == targetName) { srcQualifier = getCRSQualifierStr(source); targetQualifier = getCRSQualifierStr(target); if (strcmp(srcQualifier, targetQualifier) == 0) { srcQualifier = ""; targetQualifier = ""; } } res += " from "; res += srcName; res += srcQualifier; res += " to "; res += targetName; res += targetQualifier; return res; } // --------------------------------------------------------------------------- ConversionNNPtr Conversion::createGeographicGeocentric(const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { auto properties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildOpName("Conversion", sourceCRS, targetCRS)); auto conv = createGeographicGeocentric(properties); conv->setCRSs(sourceCRS, targetCRS, nullptr); return conv; } // --------------------------------------------------------------------------- static util::PropertyMap &addDomains(util::PropertyMap &map, const common::ObjectUsage *obj) { auto ar = util::ArrayOfBaseObject::create(); for (const auto &domain : obj->domains()) { ar->add(domain); } if (!ar->empty()) { map.set(common::ObjectUsage::OBJECT_DOMAIN_KEY, ar); } return map; } // --------------------------------------------------------------------------- static void addModifiedIdentifier(util::PropertyMap &map, const common::IdentifiedObject *obj, bool inverse, bool derivedFrom) { // If original operation is AUTH:CODE, then assign INVERSE(AUTH):CODE // as identifier. auto ar = util::ArrayOfBaseObject::create(); for (const auto &idSrc : obj->identifiers()) { auto authName = *(idSrc->codeSpace()); const auto &srcCode = idSrc->code(); if (derivedFrom) { authName = concat("DERIVED_FROM(", authName, ")"); } if (inverse) { if (starts_with(authName, "INVERSE(") && authName.back() == ')') { authName = authName.substr(strlen("INVERSE(")); authName.resize(authName.size() - 1); } else { authName = concat("INVERSE(", authName, ")"); } } auto idsProp = util::PropertyMap().set( metadata::Identifier::CODESPACE_KEY, authName); ar->add(metadata::Identifier::create(srcCode, idsProp)); } if (!ar->empty()) { map.set(common::IdentifiedObject::IDENTIFIERS_KEY, ar); } } // --------------------------------------------------------------------------- static util::PropertyMap createPropertiesForInverse(const OperationMethodNNPtr &method) { util::PropertyMap map; const std::string &forwardName = method->nameStr(); if (!forwardName.empty()) { if (starts_with(forwardName, INVERSE_OF)) { map.set(common::IdentifiedObject::NAME_KEY, forwardName.substr(INVERSE_OF.size())); } else { map.set(common::IdentifiedObject::NAME_KEY, INVERSE_OF + forwardName); } } addModifiedIdentifier(map, method.get(), true, false); return map; } // --------------------------------------------------------------------------- InverseConversion::InverseConversion(const ConversionNNPtr &forward) : Conversion( OperationMethod::create(createPropertiesForInverse(forward->method()), forward->method()->parameters()), forward->parameterValues()), InverseCoordinateOperation(forward, true) { setPropertiesFromForward(); } // --------------------------------------------------------------------------- InverseConversion::~InverseConversion() = default; // --------------------------------------------------------------------------- ConversionNNPtr InverseConversion::inverseAsConversion() const { return NN_NO_CHECK( util::nn_dynamic_pointer_cast(forwardOperation_)); } // --------------------------------------------------------------------------- CoordinateOperationNNPtr InverseConversion::create(const ConversionNNPtr &forward) { auto conv = util::nn_make_shared(forward); conv->assignSelf(conv); return conv; } // --------------------------------------------------------------------------- CoordinateOperationNNPtr InverseConversion::_shallowClone() const { auto op = InverseConversion::nn_make_shared( inverseAsConversion()->shallowClone()); op->assignSelf(op); op->setCRSs(this, false); return util::nn_static_pointer_cast(op); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool isAxisOrderReversal2D(int methodEPSGCode) { return methodEPSGCode == EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_2D; } static bool isAxisOrderReversal3D(int methodEPSGCode) { return methodEPSGCode == EPSG_CODE_METHOD_AXIS_ORDER_REVERSAL_3D; } bool isAxisOrderReversal(int methodEPSGCode) { return isAxisOrderReversal2D(methodEPSGCode) || isAxisOrderReversal3D(methodEPSGCode); } //! @endcond // --------------------------------------------------------------------------- CoordinateOperationNNPtr Conversion::inverse() const { const int methodEPSGCode = method()->getEPSGCode(); if (methodEPSGCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT) { const double convFactor = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR); auto conv = createChangeVerticalUnit( createPropertiesForInverse(this, false, false), common::Scale(1.0 / convFactor)); conv->setCRSs(this, true); return conv; } const bool l_isAxisOrderReversal2D = isAxisOrderReversal2D(methodEPSGCode); const bool l_isAxisOrderReversal3D = isAxisOrderReversal3D(methodEPSGCode); if (l_isAxisOrderReversal2D || l_isAxisOrderReversal3D) { auto conv = createAxisOrderReversal(l_isAxisOrderReversal3D); conv->setCRSs(this, true); return conv; } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC_GEOCENTRIC) { auto conv = createGeographicGeocentric( createPropertiesForInverse(this, false, false)); conv->setCRSs(this, true); return conv; } if (methodEPSGCode == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) { auto conv = createHeightDepthReversal( createPropertiesForInverse(this, false, false)); conv->setCRSs(this, true); return conv; } return InverseConversion::create(NN_NO_CHECK( util::nn_dynamic_pointer_cast(shared_from_this()))); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static double msfn(double phi, double e2) { const double sinphi = std::sin(phi); const double cosphi = std::cos(phi); return pj_msfn(sinphi, cosphi, e2); } // --------------------------------------------------------------------------- static double tsfn(double phi, double ec) { const double sinphi = std::sin(phi); return pj_tsfn(phi, sinphi, ec); } // --------------------------------------------------------------------------- // Function whose zeroes are the sin of the standard parallels of LCC_2SP static double lcc_1sp_to_2sp_f(double sinphi, double K, double ec, double n) { const double x = sinphi; const double ecx = ec * x; return (1 - x * x) / (1 - ecx * ecx) - K * K * std::pow((1.0 - x) / (1.0 + x) * std::pow((1.0 + ecx) / (1.0 - ecx), ec), n); } // --------------------------------------------------------------------------- // Find the sin of the standard parallels of LCC_2SP static double find_zero_lcc_1sp_to_2sp_f(double sinphi0, bool bNorth, double K, double ec) { double a, b; double f_a; if (bNorth) { // Look for zero above phi0 a = sinphi0; b = 1.0; // sin(North pole) f_a = 1.0; // some positive value, but we only care about the sign } else { // Look for zero below phi0 a = -1.0; // sin(South pole) b = sinphi0; f_a = -1.0; // minus infinity in fact, but we only care about the sign } // We use dichotomy search. lcc_1sp_to_2sp_f() is positive at sinphi_init, // has a zero in ]-1,sinphi0[ and ]sinphi0,1[ ranges for (int N = 0; N < 100; N++) { double c = (a + b) / 2; double f_c = lcc_1sp_to_2sp_f(c, K, ec, sinphi0); if (f_c == 0.0 || (b - a) < 1e-18) { return c; } if ((f_c > 0 && f_a > 0) || (f_c < 0 && f_a < 0)) { a = c; f_a = f_c; } else { b = c; } } return (a + b) / 2; } static inline double DegToRad(double x) { return x / 180.0 * M_PI; } static inline double RadToDeg(double x) { return x / M_PI * 180.0; } //! @endcond // --------------------------------------------------------------------------- /** * \brief Return an equivalent projection. * * Currently implemented: *
    *
  • EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP) to * EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP)
  • *
  • EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP) to * EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP)
  • *
  • EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP to * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP
  • *
  • EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP to * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP
  • *
* * @param targetEPSGCode EPSG code of the target method. * @return new conversion, or nullptr */ ConversionPtr Conversion::convertToOtherMethod(int targetEPSGCode) const { const int current_epsg_code = method()->getEPSGCode(); if (current_epsg_code == targetEPSGCode) { return util::nn_dynamic_pointer_cast(shared_from_this()); } auto geogCRS = dynamic_cast(sourceCRS().get()); if (!geogCRS) { return nullptr; } const double e2 = geogCRS->ellipsoid()->squaredEccentricity(); if (e2 < 0) { return nullptr; } if (current_epsg_code == EPSG_CODE_METHOD_MERCATOR_VARIANT_A && targetEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && parameterValueNumericAsSI( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) == 0.0) { const double k0 = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); if (!(k0 > 0 && k0 <= 1.0 + 1e-10)) return nullptr; const double dfStdP1Lat = (k0 >= 1.0) ? 0.0 : std::acos(std::sqrt((1.0 - e2) / ((1.0 / (k0 * k0)) - e2))); auto latitudeFirstParallel = common::Angle( common::Angle(dfStdP1Lat, common::UnitOfMeasure::RADIAN) .convertToUnit(common::UnitOfMeasure::DEGREE), common::UnitOfMeasure::DEGREE); auto conv = createMercatorVariantB( util::PropertyMap(), latitudeFirstParallel, common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN)), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_EASTING)), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_NORTHING))); conv->setCRSs(this, false); return conv.as_nullable(); } if (current_epsg_code == EPSG_CODE_METHOD_MERCATOR_VARIANT_B && targetEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A) { const double phi1 = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL); if (!(std::fabs(phi1) < M_PI / 2)) return nullptr; const double k0 = msfn(phi1, e2); auto conv = createMercatorVariantA( util::PropertyMap(), common::Angle(0.0, common::UnitOfMeasure::DEGREE), common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN)), common::Scale(k0, common::UnitOfMeasure::SCALE_UNITY), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_EASTING)), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_NORTHING))); conv->setCRSs(this, false); return conv.as_nullable(); } if (current_epsg_code == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP && targetEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP) { // Notations m0, t0, n, m1, t1, F are those of the EPSG guidance // "1.3.1.1 Lambert Conic Conformal (2SP)" and // "1.3.1.2 Lambert Conic Conformal (1SP)" and // or Snyder pages 106-109 auto latitudeOfOrigin = common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN)); const double phi0 = latitudeOfOrigin.getSIValue(); const double k0 = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN); if (!(std::fabs(phi0) < M_PI / 2)) return nullptr; if (!(k0 > 0 && k0 <= 1.0 + 1e-10)) return nullptr; const double ec = std::sqrt(e2); const double m0 = msfn(phi0, e2); const double t0 = tsfn(phi0, ec); const double n = sin(phi0); if (std::fabs(n) < 1e-10) return nullptr; if (fabs(k0 - 1.0) <= 1e-10) { auto conv = createLambertConicConformal_2SP( util::PropertyMap(), latitudeOfOrigin, common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN)), latitudeOfOrigin, latitudeOfOrigin, common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_EASTING)), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_NORTHING))); conv->setCRSs(this, false); return conv.as_nullable(); } else { const double K = k0 * m0 / std::pow(t0, n); const double phi1 = std::asin(find_zero_lcc_1sp_to_2sp_f(n, true, K, ec)); const double phi2 = std::asin(find_zero_lcc_1sp_to_2sp_f(n, false, K, ec)); double phi1Deg = RadToDeg(phi1); double phi2Deg = RadToDeg(phi2); // Try to round to hundreth of degree if very close to it if (std::fabs(phi1Deg * 1000 - std::floor(phi1Deg * 1000 + 0.5)) < 1e-8) phi1Deg = floor(phi1Deg * 1000 + 0.5) / 1000; if (std::fabs(phi2Deg * 1000 - std::floor(phi2Deg * 1000 + 0.5)) < 1e-8) phi2Deg = std::floor(phi2Deg * 1000 + 0.5) / 1000; // The following improvement is too turn the LCC1SP equivalent of // EPSG:2154 to the real LCC2SP // If the computed latitude of origin is close to .0 or .5 degrees // then check if rounding it to it will get a false northing // close to an integer const double FN = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_FALSE_NORTHING); const double latitudeOfOriginDeg = latitudeOfOrigin.convertToUnit(common::UnitOfMeasure::DEGREE); if (std::fabs(latitudeOfOriginDeg * 2 - std::floor(latitudeOfOriginDeg * 2 + 0.5)) < 0.2) { const double dfRoundedLatOfOrig = std::floor(latitudeOfOriginDeg * 2 + 0.5) / 2; const double m1 = msfn(phi1, e2); const double t1 = tsfn(phi1, ec); const double F = m1 / (n * std::pow(t1, n)); const double a = geogCRS->ellipsoid()->semiMajorAxis().getSIValue(); const double tRoundedLatOfOrig = tsfn(DegToRad(dfRoundedLatOfOrig), ec); const double FN_correction = a * F * (std::pow(tRoundedLatOfOrig, n) - std::pow(t0, n)); const double FN_corrected = FN - FN_correction; const double FN_corrected_rounded = std::floor(FN_corrected + 0.5); if (std::fabs(FN_corrected - FN_corrected_rounded) < 1e-8) { auto conv = createLambertConicConformal_2SP( util::PropertyMap(), common::Angle(dfRoundedLatOfOrig, common::UnitOfMeasure::DEGREE), common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN)), common::Angle(phi1Deg, common::UnitOfMeasure::DEGREE), common::Angle(phi2Deg, common::UnitOfMeasure::DEGREE), common::Length(parameterValueMeasure( EPSG_CODE_PARAMETER_FALSE_EASTING)), common::Length(FN_corrected_rounded)); conv->setCRSs(this, false); return conv.as_nullable(); } } auto conv = createLambertConicConformal_2SP( util::PropertyMap(), latitudeOfOrigin, common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN)), common::Angle(phi1Deg, common::UnitOfMeasure::DEGREE), common::Angle(phi2Deg, common::UnitOfMeasure::DEGREE), common::Length( parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_EASTING)), common::Length(FN)); conv->setCRSs(this, false); return conv.as_nullable(); } } if (current_epsg_code == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP && targetEPSGCode == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP) { // Notations m0, t0, m1, t1, m2, t2 n, F are those of the EPSG guidance // "1.3.1.1 Lambert Conic Conformal (2SP)" and // "1.3.1.2 Lambert Conic Conformal (1SP)" and // or Snyder pages 106-109 const double phiF = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN) .getSIValue(); const double phi1 = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL) .getSIValue(); const double phi2 = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) .getSIValue(); if (!(std::fabs(phiF) < M_PI / 2)) return nullptr; if (!(std::fabs(phi1) < M_PI / 2)) return nullptr; if (!(std::fabs(phi2) < M_PI / 2)) return nullptr; const double ec = std::sqrt(e2); const double m1 = msfn(phi1, e2); const double m2 = msfn(phi2, e2); const double t1 = tsfn(phi1, ec); const double t2 = tsfn(phi2, ec); const double n_denom = std::log(t1) - std::log(t2); const double n = (std::fabs(n_denom) < 1e-10) ? std::sin(phi1) : (std::log(m1) - std::log(m2)) / n_denom; if (std::fabs(n) < 1e-10) return nullptr; const double F = m1 / (n * std::pow(t1, n)); const double phi0 = std::asin(n); const double m0 = msfn(phi0, e2); const double t0 = tsfn(phi0, ec); const double F0 = m0 / (n * std::pow(t0, n)); const double k0 = F / F0; const double a = geogCRS->ellipsoid()->semiMajorAxis().getSIValue(); const double tF = tsfn(phiF, ec); const double FN_correction = a * F * (std::pow(tF, n) - std::pow(t0, n)); double phi0Deg = RadToDeg(phi0); // Try to round to thousandth of degree if very close to it if (std::fabs(phi0Deg * 1000 - std::floor(phi0Deg * 1000 + 0.5)) < 1e-8) phi0Deg = std::floor(phi0Deg * 1000 + 0.5) / 1000; auto conv = createLambertConicConformal_1SP( util::PropertyMap(), common::Angle(phi0Deg, common::UnitOfMeasure::DEGREE), common::Angle(parameterValueMeasure( EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN)), common::Scale(k0), common::Length(parameterValueMeasure( EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN)), common::Length( parameterValueNumericAsSI( EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN) + (std::fabs(FN_correction) > 1e-8 ? FN_correction : 0))); conv->setCRSs(this, false); return conv.as_nullable(); } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static void getESRIMethodNameAndParams(const Conversion *conv, const std::string &methodName, int methodEPSGCode, const char *&esriMethodName, const ESRIParamMapping *&esriParams) { esriParams = nullptr; esriMethodName = nullptr; const auto *esriMapping = getESRIMapping(methodName, methodEPSGCode); const auto l_targetCRS = conv->targetCRS(); if (esriMapping) { esriParams = esriMapping->params; esriMethodName = esriMapping->esri_name; if (esriMapping->epsg_code == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL || esriMapping->epsg_code == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL) { if (l_targetCRS && ci_find(l_targetCRS->nameStr(), "Plate Carree") != std::string::npos && conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) == 0.0) { esriParams = paramsESRI_Plate_Carree; esriMethodName = "Plate_Carree"; } else { esriParams = paramsESRI_Equidistant_Cylindrical; esriMethodName = "Equidistant_Cylindrical"; } } else if (esriMapping->epsg_code == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) { if (ci_find(conv->nameStr(), "Gauss Kruger") != std::string::npos || (l_targetCRS && (ci_find(l_targetCRS->nameStr(), "Gauss") != std::string::npos || ci_find(l_targetCRS->nameStr(), "GK_") != std::string::npos))) { esriParams = paramsESRI_Gauss_Kruger; esriMethodName = "Gauss_Kruger"; } else { esriParams = paramsESRI_Transverse_Mercator; esriMethodName = "Transverse_Mercator"; } } else if (esriMapping->epsg_code == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A) { if (std::abs( conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE) - conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID)) < 1e-15) { esriParams = paramsESRI_Hotine_Oblique_Mercator_Azimuth_Natural_Origin; esriMethodName = "Hotine_Oblique_Mercator_Azimuth_Natural_Origin"; } else { esriParams = paramsESRI_Rectified_Skew_Orthomorphic_Natural_Origin; esriMethodName = "Rectified_Skew_Orthomorphic_Natural_Origin"; } } else if (esriMapping->epsg_code == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B) { if (std::abs( conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE) - conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID)) < 1e-15) { esriParams = paramsESRI_Hotine_Oblique_Mercator_Azimuth_Center; esriMethodName = "Hotine_Oblique_Mercator_Azimuth_Center"; } else { esriParams = paramsESRI_Rectified_Skew_Orthomorphic_Center; esriMethodName = "Rectified_Skew_Orthomorphic_Center"; } } else if (esriMapping->epsg_code == EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B) { if (conv->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_LATITUDE_STD_PARALLEL) > 0) { esriMethodName = "Stereographic_North_Pole"; } else { esriMethodName = "Stereographic_South_Pole"; } } } } // --------------------------------------------------------------------------- const char *Conversion::getESRIMethodName() const { const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const auto methodEPSGCode = l_method->getEPSGCode(); const ESRIParamMapping *esriParams = nullptr; const char *esriMethodName = nullptr; getESRIMethodNameAndParams(this, methodName, methodEPSGCode, esriMethodName, esriParams); return esriMethodName; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const char *Conversion::getWKT1GDALMethodName() const { const auto &l_method = method(); const auto methodEPSGCode = l_method->getEPSGCode(); if (methodEPSGCode == EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR) { return "Mercator_1SP"; } const MethodMapping *mapping = getMapping(l_method.get()); return mapping ? mapping->wkt1_name : nullptr; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Conversion::_exportToWKT(io::WKTFormatter *formatter) const { const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const auto methodEPSGCode = l_method->getEPSGCode(); const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2 && formatter->useESRIDialect()) { if (methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A) { auto eqConv = convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_B); if (eqConv) { eqConv->_exportToWKT(formatter); return; } } } if (isWKT2) { formatter->startNode(formatter->useDerivingConversion() ? io::WKTConstants::DERIVINGCONVERSION : io::WKTConstants::CONVERSION, !identifiers().empty()); formatter->addQuotedString(nameStr()); } else { formatter->enter(); formatter->pushOutputUnit(false); formatter->pushOutputId(false); } #ifdef DEBUG_CONVERSION_ID if (sourceCRS() && targetCRS()) { formatter->startNode("SOURCECRS_ID", false); sourceCRS()->formatID(formatter); formatter->endNode(); formatter->startNode("TARGETCRS_ID", false); targetCRS()->formatID(formatter); formatter->endNode(); } #endif bool bAlreadyWritten = false; if (!isWKT2 && formatter->useESRIDialect()) { const ESRIParamMapping *esriParams = nullptr; const char *esriMethodName = nullptr; getESRIMethodNameAndParams(this, methodName, methodEPSGCode, esriMethodName, esriParams); if (esriMethodName && esriParams) { formatter->startNode(io::WKTConstants::PROJECTION, false); formatter->addQuotedString(esriMethodName); formatter->endNode(); for (int i = 0; esriParams[i].esri_name != nullptr; i++) { const auto &esriParam = esriParams[i]; formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString(esriParam.esri_name); if (esriParam.wkt2_name) { const auto &pv = parameterValue(esriParam.wkt2_name, esriParam.epsg_code); if (pv && pv->type() == ParameterValue::Type::MEASURE) { const auto &v = pv->value(); // as we don't output the natural unit, output // to the registered linear / angular unit. const auto &unitType = v.unit().type(); if (unitType == common::UnitOfMeasure::Type::LINEAR) { formatter->add(v.convertToUnit( *(formatter->axisLinearUnit()))); } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) { const auto &angUnit = *(formatter->axisAngularUnit()); double val = v.convertToUnit(angUnit); if (angUnit == common::UnitOfMeasure::DEGREE) { if (val > 180.0) { val -= 360.0; } else if (val < -180.0) { val += 360.0; } } formatter->add(val); } else { formatter->add(v.getSIValue()); } } else if (ci_find(esriParam.esri_name, "scale") != std::string::npos) { formatter->add(1.0); } else { formatter->add(0.0); } } else { formatter->add(esriParam.fixed_value); } formatter->endNode(); } bAlreadyWritten = true; } } else if (!isWKT2) { if (methodEPSGCode == EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR) { const double latitudeOrigin = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); if (latitudeOrigin != 0) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); } bAlreadyWritten = true; formatter->startNode(io::WKTConstants::PROJECTION, false); formatter->addQuotedString("Mercator_1SP"); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("central_meridian"); const double centralMeridian = parameterValueNumeric( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); formatter->add(centralMeridian); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("scale_factor"); formatter->add(1.0); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("false_easting"); const double falseEasting = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_FALSE_EASTING); formatter->add(falseEasting); formatter->endNode(); formatter->startNode(io::WKTConstants::PARAMETER, false); formatter->addQuotedString("false_northing"); const double falseNorthing = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_FALSE_NORTHING); formatter->add(falseNorthing); formatter->endNode(); } else if (starts_with(methodName, "PROJ ")) { bAlreadyWritten = true; formatter->startNode(io::WKTConstants::PROJECTION, false); formatter->addQuotedString("custom_proj4"); formatter->endNode(); } } if (!bAlreadyWritten) { l_method->_exportToWKT(formatter); const MethodMapping *mapping = !isWKT2 ? getMapping(l_method.get()) : nullptr; for (const auto &genOpParamvalue : parameterValues()) { // EPSG has normally no Latitude of natural origin for Equidistant // Cylindrical but PROJ can handle it, so output the parameter if // not zero if ((methodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL || methodEPSGCode == EPSG_CODE_METHOD_EQUIDISTANT_CYLINDRICAL_SPHERICAL)) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue && opParamvalue->parameter()->getEPSGCode() == EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN) { const auto ¶mValue = opParamvalue->parameterValue(); if (paramValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = paramValue->value(); if (measure.getSIValue() == 0) { continue; } } } } // Same for false easting / false northing for Vertical Perspective else if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_PERSPECTIVE) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto paramEPSGCode = opParamvalue->parameter()->getEPSGCode(); if (paramEPSGCode == EPSG_CODE_PARAMETER_FALSE_EASTING || paramEPSGCode == EPSG_CODE_PARAMETER_FALSE_NORTHING) { const auto ¶mValue = opParamvalue->parameterValue(); if (paramValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = paramValue->value(); if (measure.getSIValue() == 0) { continue; } } } } } genOpParamvalue->_exportToWKT(formatter, mapping); } } if (isWKT2) { if (formatter->outputId()) { formatID(formatter); } formatter->endNode(); } else { formatter->popOutputUnit(); formatter->popOutputId(); formatter->leave(); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Conversion::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext( formatter->MakeObjectContext("Conversion", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("method"); formatter->setOmitTypeInImmediateChild(); formatter->setAllowIDInImmediateChild(); method()->_exportToJSON(formatter); const auto &l_parameterValues = parameterValues(); if (!l_parameterValues.empty()) { writer.AddObjKey("parameters"); { auto parametersContext(writer.MakeArrayContext(false)); for (const auto &genOpParamvalue : l_parameterValues) { formatter->setAllowIDInImmediateChild(); formatter->setOmitTypeInImmediateChild(); genOpParamvalue->_exportToJSON(formatter); } } } if (formatter->outputId()) { formatID(formatter); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool createPROJ4WebMercator(const Conversion *conv, io::PROJStringFormatter *formatter) { const double centralMeridian = conv->parameterValueNumeric( EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); const double falseEasting = conv->parameterValueNumericAsSI(EPSG_CODE_PARAMETER_FALSE_EASTING); const double falseNorthing = conv->parameterValueNumericAsSI(EPSG_CODE_PARAMETER_FALSE_NORTHING); auto sourceCRS = conv->sourceCRS(); auto geogCRS = dynamic_cast(sourceCRS.get()); if (!geogCRS) { return false; } formatter->addStep("merc"); const double a = geogCRS->ellipsoid()->semiMajorAxis().getSIValue(); formatter->addParam("a", a); formatter->addParam("b", a); formatter->addParam("lat_ts", 0.0); formatter->addParam("lon_0", centralMeridian); formatter->addParam("x_0", falseEasting); formatter->addParam("y_0", falseNorthing); formatter->addParam("k", 1.0); formatter->addParam("units", "m"); formatter->addParam("nadgrids", "@null"); formatter->addParam("wktext"); formatter->addParam("no_defs"); return true; } // --------------------------------------------------------------------------- static bool createPROJExtensionFromCustomProj(const Conversion *conv, io::PROJStringFormatter *formatter, bool forExtensionNode) { const auto &methodName = conv->method()->nameStr(); assert(starts_with(methodName, "PROJ ")); auto tokens = split(methodName, ' '); formatter->addStep(tokens[1]); if (forExtensionNode) { auto sourceCRS = conv->sourceCRS(); auto geogCRS = dynamic_cast(sourceCRS.get()); if (!geogCRS) { return false; } geogCRS->addDatumInfoToPROJString(formatter); } for (size_t i = 2; i < tokens.size(); i++) { auto kv = split(tokens[i], '='); if (kv.size() == 2) { formatter->addParam(kv[0], kv[1]); } else { formatter->addParam(tokens[i]); } } for (const auto &genOpParamvalue : conv->parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶mName = opParamvalue->parameter()->nameStr(); const auto ¶mValue = opParamvalue->parameterValue(); if (paramValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = paramValue->value(); const auto unitType = measure.unit().type(); if (unitType == common::UnitOfMeasure::Type::LINEAR) { formatter->addParam(paramName, measure.getSIValue()); } else if (unitType == common::UnitOfMeasure::Type::ANGULAR) { formatter->addParam( paramName, measure.convertToUnit(common::UnitOfMeasure::DEGREE)); } else { formatter->addParam(paramName, measure.value()); } } } } if (forExtensionNode) { formatter->addParam("wktext"); formatter->addParam("no_defs"); } return true; } //! @endcond // --------------------------------------------------------------------------- bool Conversion::addWKTExtensionNode(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const int methodEPSGCode = l_method->getEPSGCode(); if (l_method->getPrivate()->projMethodOverride_ == "tmerc approx" || l_method->getPrivate()->projMethodOverride_ == "utm approx") { auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); projFormatter->setUseApproxTMerc(true); formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); _exportToPROJString(projFormatter.get()); projFormatter->addParam("no_defs"); formatter->addQuotedString(projFormatter->toString()); formatter->endNode(); return true; } else if (methodEPSGCode == EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR || nameStr() == "Popular Visualisation Mercator") { auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); if (createPROJ4WebMercator(this, projFormatter.get())) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); formatter->addQuotedString(projFormatter->toString()); formatter->endNode(); return true; } } else if (starts_with(methodName, "PROJ ")) { auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); if (createPROJExtensionFromCustomProj(this, projFormatter.get(), true)) { formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); formatter->addQuotedString(projFormatter->toString()); formatter->endNode(); return true; } } else if (methodName == PROJ_WKT2_NAME_METHOD_GEOSTATIONARY_SATELLITE_SWEEP_X) { auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); formatter->startNode(io::WKTConstants::EXTENSION, false); formatter->addQuotedString("PROJ4"); _exportToPROJString(projFormatter.get()); projFormatter->addParam("no_defs"); formatter->addQuotedString(projFormatter->toString()); formatter->endNode(); return true; } } return false; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Conversion::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const int methodEPSGCode = l_method->getEPSGCode(); const bool isZUnitConversion = methodEPSGCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT; const bool isAffineParametric = methodEPSGCode == EPSG_CODE_METHOD_AFFINE_PARAMETRIC_TRANSFORMATION; const bool isGeographicGeocentric = methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC_GEOCENTRIC; const bool isHeightDepthReversal = methodEPSGCode == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL; const bool applySourceCRSModifiers = !isZUnitConversion && !isAffineParametric && !isAxisOrderReversal(methodEPSGCode) && !isGeographicGeocentric && !isHeightDepthReversal; bool applyTargetCRSModifiers = applySourceCRSModifiers; auto l_sourceCRS = sourceCRS(); if (!formatter->getCRSExport() && l_sourceCRS && applySourceCRSModifiers) { crs::CRS *horiz = l_sourceCRS.get(); const auto compound = dynamic_cast(horiz); if (compound) { const auto &components = compound->componentReferenceSystems(); if (!components.empty()) { horiz = components.front().get(); } } auto geogCRS = dynamic_cast(horiz); if (geogCRS) { formatter->setOmitProjLongLatIfPossible(true); formatter->startInversion(); geogCRS->_exportToPROJString(formatter); formatter->stopInversion(); formatter->setOmitProjLongLatIfPossible(false); } auto projCRS = dynamic_cast(horiz); if (projCRS) { formatter->startInversion(); formatter->pushOmitZUnitConversion(); projCRS->addUnitConvertAndAxisSwap(formatter, false); formatter->popOmitZUnitConversion(); formatter->stopInversion(); } } const auto &convName = nameStr(); bool bConversionDone = false; bool bEllipsoidParametersDone = false; bool useApprox = false; if (methodEPSGCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) { // Check for UTM int zone = 0; bool north = true; useApprox = formatter->getUseApproxTMerc() || l_method->getPrivate()->projMethodOverride_ == "tmerc approx" || l_method->getPrivate()->projMethodOverride_ == "utm approx"; if (isUTM(zone, north)) { bConversionDone = true; formatter->addStep("utm"); if (useApprox) { formatter->addParam("approx"); } formatter->addParam("zone", zone); if (!north) { formatter->addParam("south"); } } } else if (methodEPSGCode == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_A) { const double azimuth = parameterValueNumeric(EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE, common::UnitOfMeasure::DEGREE); const double angleRectifiedToSkewGrid = parameterValueNumeric( EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID, common::UnitOfMeasure::DEGREE); // Map to Swiss Oblique Mercator / somerc if (std::fabs(azimuth - 90) < 1e-4 && std::fabs(angleRectifiedToSkewGrid - 90) < 1e-4) { bConversionDone = true; formatter->addStep("somerc"); formatter->addParam( "lat_0", parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_PROJECTION_CENTRE, common::UnitOfMeasure::DEGREE)); formatter->addParam( "lon_0", parameterValueNumeric( EPSG_CODE_PARAMETER_LONGITUDE_PROJECTION_CENTRE, common::UnitOfMeasure::DEGREE)); formatter->addParam( "k_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SCALE_FACTOR_INITIAL_LINE)); formatter->addParam("x_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_FALSE_EASTING)); formatter->addParam("y_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_FALSE_NORTHING)); } } else if (methodEPSGCode == EPSG_CODE_METHOD_HOTINE_OBLIQUE_MERCATOR_VARIANT_B) { const double azimuth = parameterValueNumeric(EPSG_CODE_PARAMETER_AZIMUTH_INITIAL_LINE, common::UnitOfMeasure::DEGREE); const double angleRectifiedToSkewGrid = parameterValueNumeric( EPSG_CODE_PARAMETER_ANGLE_RECTIFIED_TO_SKEW_GRID, common::UnitOfMeasure::DEGREE); // Map to Swiss Oblique Mercator / somerc if (std::fabs(azimuth - 90) < 1e-4 && std::fabs(angleRectifiedToSkewGrid - 90) < 1e-4) { bConversionDone = true; formatter->addStep("somerc"); formatter->addParam( "lat_0", parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_PROJECTION_CENTRE, common::UnitOfMeasure::DEGREE)); formatter->addParam( "lon_0", parameterValueNumeric( EPSG_CODE_PARAMETER_LONGITUDE_PROJECTION_CENTRE, common::UnitOfMeasure::DEGREE)); formatter->addParam( "k_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SCALE_FACTOR_INITIAL_LINE)); formatter->addParam( "x_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_EASTING_PROJECTION_CENTRE)); formatter->addParam( "y_0", parameterValueNumericAsSI( EPSG_CODE_PARAMETER_NORTHING_PROJECTION_CENTRE)); } } else if (methodEPSGCode == EPSG_CODE_METHOD_KROVAK_NORTH_ORIENTED) { double colatitude = parameterValueNumeric(EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS, common::UnitOfMeasure::DEGREE); double latitudePseudoStandardParallel = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL, common::UnitOfMeasure::DEGREE); // 30deg 17' 17.30311'' = 30.28813975277777776 // 30deg 17' 17.303'' = 30.288139722222223 as used in GDAL WKT1 if (std::fabs(colatitude - 30.2881397) > 1e-7) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_COLATITUDE_CONE_AXIS); } if (std::fabs(latitudePseudoStandardParallel - 78.5) > 1e-8) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL); } } else if (methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A) { double latitudeOrigin = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); if (latitudeOrigin != 0) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); } } else if (methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_B) { const auto &scaleFactor = parameterValueMeasure(WKT1_SCALE_FACTOR, 0); if (scaleFactor.unit().type() != common::UnitOfMeasure::Type::UNKNOWN && std::fabs(scaleFactor.getSIValue() - 1.0) > 1e-10) { throw io::FormattingException( "Unexpected presence of scale factor in Mercator (variant B)"); } double latitudeOrigin = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, common::UnitOfMeasure::DEGREE); if (latitudeOrigin != 0) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); } // PROJ.4 specific hack for webmercator } else if (formatter->getCRSExport() && methodEPSGCode == EPSG_CODE_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR) { if (!createPROJ4WebMercator(this, formatter)) { throw io::FormattingException( std::string("Cannot export ") + EPSG_NAME_METHOD_POPULAR_VISUALISATION_PSEUDO_MERCATOR + " as PROJ.4 string outside of a ProjectedCRS context"); } bConversionDone = true; bEllipsoidParametersDone = true; applyTargetCRSModifiers = false; } else if (ci_equal(convName, "Popular Visualisation Mercator")) { if (formatter->getCRSExport()) { if (!createPROJ4WebMercator(this, formatter)) { throw io::FormattingException(concat( "Cannot export ", convName, " as PROJ.4 string outside of a ProjectedCRS context")); } applyTargetCRSModifiers = false; } else { formatter->addStep("webmerc"); if (l_sourceCRS) { datum::Ellipsoid::WGS84->_exportToPROJString(formatter); } } bConversionDone = true; bEllipsoidParametersDone = true; } else if (starts_with(methodName, "PROJ ")) { bConversionDone = true; createPROJExtensionFromCustomProj(this, formatter, false); } else if (ci_equal(methodName, PROJ_WKT2_NAME_METHOD_POLE_ROTATION_GRIB_CONVENTION)) { double southPoleLat = parameterValueNumeric( PROJ_WKT2_NAME_PARAMETER_SOUTH_POLE_LATITUDE_GRIB_CONVENTION, common::UnitOfMeasure::DEGREE); double southPoleLon = parameterValueNumeric( PROJ_WKT2_NAME_PARAMETER_SOUTH_POLE_LONGITUDE_GRIB_CONVENTION, common::UnitOfMeasure::DEGREE); double rotation = parameterValueNumeric( PROJ_WKT2_NAME_PARAMETER_AXIS_ROTATION_GRIB_CONVENTION, common::UnitOfMeasure::DEGREE); formatter->addStep("ob_tran"); formatter->addParam("o_proj", "longlat"); formatter->addParam("o_lon_p", -rotation); formatter->addParam("o_lat_p", -southPoleLat); formatter->addParam("lon_0", southPoleLon); bConversionDone = true; } else if (formatter->convention() == io::PROJStringFormatter::Convention::PROJ_5 && isZUnitConversion) { double convFactor = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR); auto uom = common::UnitOfMeasure(std::string(), convFactor, common::UnitOfMeasure::Type::LINEAR) .exportToPROJString(); auto reverse_uom = common::UnitOfMeasure(std::string(), 1.0 / convFactor, common::UnitOfMeasure::Type::LINEAR) .exportToPROJString(); if (uom == "m") { // do nothing } else if (!uom.empty()) { formatter->addStep("unitconvert"); formatter->addParam("z_in", uom); formatter->addParam("z_out", "m"); } else if (!reverse_uom.empty()) { formatter->addStep("unitconvert"); formatter->addParam("z_in", "m"); formatter->addParam("z_out", reverse_uom); } else { formatter->addStep("affine"); formatter->addParam("s33", convFactor); } bConversionDone = true; bEllipsoidParametersDone = true; } bool bAxisSpecFound = false; if (!bConversionDone) { const MethodMapping *mapping = getMapping(l_method.get()); if (mapping && mapping->proj_name_main) { formatter->addStep(mapping->proj_name_main); if (useApprox) { formatter->addParam("approx"); } if (mapping->proj_name_aux) { if (internal::starts_with(mapping->proj_name_aux, "axis=")) { bAxisSpecFound = true; } auto kv = split(mapping->proj_name_aux, '='); if (kv.size() == 2) { formatter->addParam(kv[0], kv[1]); } else { formatter->addParam(mapping->proj_name_aux); } } if (mapping->epsg_code == EPSG_CODE_METHOD_POLAR_STEREOGRAPHIC_VARIANT_B) { double latitudeStdParallel = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_STD_PARALLEL, common::UnitOfMeasure::DEGREE); formatter->addParam("lat_0", (latitudeStdParallel >= 0) ? 90.0 : -90.0); } for (int i = 0; mapping->params[i] != nullptr; i++) { const auto *param = mapping->params[i]; if (!param->proj_name) { continue; } const auto value = parameterValueMeasure(param->wkt2_name, param->epsg_code); double valueConverted = 0; if (value == nullMeasure) { // Deal with missing values. In an ideal world, this would // not happen if (param->epsg_code == EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN) { valueConverted = 1.0; } } else if (param->unit_type == common::UnitOfMeasure::Type::ANGULAR) { valueConverted = value.convertToUnit(common::UnitOfMeasure::DEGREE); } else { valueConverted = value.getSIValue(); } if (mapping->epsg_code == EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP && strcmp(param->proj_name, "lat_1") == 0) { formatter->addParam(param->proj_name, valueConverted); formatter->addParam("lat_0", valueConverted); } else { formatter->addParam(param->proj_name, valueConverted); } } } else { if (!exportToPROJStringGeneric(formatter)) { throw io::FormattingException( concat("Unsupported conversion method: ", methodName)); } } } auto l_targetCRS = targetCRS(); if (l_targetCRS && applyTargetCRSModifiers) { crs::CRS *horiz = l_targetCRS.get(); const auto compound = dynamic_cast(horiz); if (compound) { const auto &components = compound->componentReferenceSystems(); if (!components.empty()) { horiz = components.front().get(); } } if (!bEllipsoidParametersDone) { auto targetGeogCRS = horiz->extractGeographicCRS(); if (targetGeogCRS) { if (formatter->getCRSExport()) { targetGeogCRS->addDatumInfoToPROJString(formatter); } else { targetGeogCRS->ellipsoid()->_exportToPROJString(formatter); targetGeogCRS->primeMeridian()->_exportToPROJString( formatter); } } } auto projCRS = dynamic_cast(horiz); if (projCRS) { formatter->pushOmitZUnitConversion(); projCRS->addUnitConvertAndAxisSwap(formatter, bAxisSpecFound); formatter->popOmitZUnitConversion(); } auto derivedGeographicCRS = dynamic_cast(horiz); if (derivedGeographicCRS) { auto baseGeodCRS = derivedGeographicCRS->baseCRS(); formatter->setOmitProjLongLatIfPossible(true); baseGeodCRS->_exportToPROJString(formatter); formatter->setOmitProjLongLatIfPossible(false); } } } //! @endcond // --------------------------------------------------------------------------- /** \brief Return whether a conversion is a [Universal Transverse Mercator] * (https://proj.org/operations/projections/utm.html) conversion. * * @param[out] zone UTM zone number between 1 and 60. * @param[out] north true for UTM northern hemisphere, false for UTM southern * hemisphere. * @return true if it is a UTM conversion. */ bool Conversion::isUTM(int &zone, bool &north) const { zone = 0; north = true; if (method()->getEPSGCode() == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) { // Check for UTM bool bLatitudeNatOriginUTM = false; bool bScaleFactorUTM = false; bool bFalseEastingUTM = false; bool bFalseNorthingUTM = false; for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto epsg_code = opParamvalue->parameter()->getEPSGCode(); const auto &l_parameterValue = opParamvalue->parameterValue(); if (l_parameterValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = l_parameterValue->value(); if (epsg_code == EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN && std::fabs(measure.value() - UTM_LATITUDE_OF_NATURAL_ORIGIN) < 1e-10) { bLatitudeNatOriginUTM = true; } else if ( (epsg_code == EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN || epsg_code == EPSG_CODE_PARAMETER_LONGITUDE_OF_ORIGIN) && measure.unit()._isEquivalentTo( common::UnitOfMeasure::DEGREE, util::IComparable::Criterion::EQUIVALENT)) { double dfZone = (measure.value() + 183.0) / 6.0; if (dfZone > 0.9 && dfZone < 60.1 && std::abs(dfZone - std::round(dfZone)) < 1e-10) { zone = static_cast(std::lround(dfZone)); } } else if ( epsg_code == EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN && measure.unit()._isEquivalentTo( common::UnitOfMeasure::SCALE_UNITY, util::IComparable::Criterion::EQUIVALENT) && std::fabs(measure.value() - UTM_SCALE_FACTOR) < 1e-10) { bScaleFactorUTM = true; } else if (epsg_code == EPSG_CODE_PARAMETER_FALSE_EASTING && measure.value() == UTM_FALSE_EASTING && measure.unit()._isEquivalentTo( common::UnitOfMeasure::METRE, util::IComparable::Criterion::EQUIVALENT)) { bFalseEastingUTM = true; } else if (epsg_code == EPSG_CODE_PARAMETER_FALSE_NORTHING && measure.unit()._isEquivalentTo( common::UnitOfMeasure::METRE, util::IComparable::Criterion::EQUIVALENT)) { if (std::fabs(measure.value() - UTM_NORTH_FALSE_NORTHING) < 1e-10) { bFalseNorthingUTM = true; north = true; } else if (std::fabs(measure.value() - UTM_SOUTH_FALSE_NORTHING) < 1e-10) { bFalseNorthingUTM = true; north = false; } } } } } if (bLatitudeNatOriginUTM && zone > 0 && bScaleFactorUTM && bFalseEastingUTM && bFalseNorthingUTM) { return true; } } return false; } // --------------------------------------------------------------------------- /** \brief Return a Conversion object where some parameters are better * identified. * * @return a new Conversion. */ ConversionNNPtr Conversion::identify() const { auto newConversion = Conversion::nn_make_shared(*this); newConversion->assignSelf(newConversion); if (method()->getEPSGCode() == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) { // Check for UTM int zone = 0; bool north = true; if (isUTM(zone, north)) { newConversion->setProperties( getUTMConversionProperty(util::PropertyMap(), zone, north)); } } return newConversion; } //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- InvalidOperation::InvalidOperation(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- InvalidOperation::InvalidOperation(const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- InvalidOperation::InvalidOperation(const InvalidOperation &) = default; // --------------------------------------------------------------------------- InvalidOperation::~InvalidOperation() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Transformation::Private { TransformationPtr forwardOperation_{}; TransformationNNPtr registerInv(util::BaseObjectNNPtr thisIn, TransformationNNPtr invTransform); }; //! @endcond // --------------------------------------------------------------------------- Transformation::Transformation( const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn, const OperationMethodNNPtr &methodIn, const std::vector &values, const std::vector &accuracies) : SingleOperation(methodIn), d(internal::make_unique()) { setParameterValues(values); setCRSs(sourceCRSIn, targetCRSIn, interpolationCRSIn); setAccuracies(accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Transformation::~Transformation() = default; //! @endcond // --------------------------------------------------------------------------- Transformation::Transformation(const Transformation &other) : CoordinateOperation(other), SingleOperation(other), d(internal::make_unique(*other.d)) {} // --------------------------------------------------------------------------- /** \brief Return the source crs::CRS of the transformation. * * @return the source CRS. */ const crs::CRSNNPtr &Transformation::sourceCRS() PROJ_PURE_DEFN { return CoordinateOperation::getPrivate()->strongRef_->sourceCRS_; } // --------------------------------------------------------------------------- /** \brief Return the target crs::CRS of the transformation. * * @return the target CRS. */ const crs::CRSNNPtr &Transformation::targetCRS() PROJ_PURE_DEFN { return CoordinateOperation::getPrivate()->strongRef_->targetCRS_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TransformationNNPtr Transformation::shallowClone() const { auto transf = Transformation::nn_make_shared(*this); transf->assignSelf(transf); transf->setCRSs(this, false); return transf; } CoordinateOperationNNPtr Transformation::_shallowClone() const { return util::nn_static_pointer_cast(shallowClone()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress /** \brief Return the TOWGS84 parameters of the transformation. * * If this transformation uses Coordinate Frame Rotation, Position Vector * transformation or Geocentric translations, a vector of 7 double values * using the Position Vector convention (EPSG:9606) is returned. Those values * can be used as the value of the WKT1 TOWGS84 parameter or * PROJ +towgs84 parameter. * * @return a vector of 7 values if valid, otherwise a io::FormattingException * is thrown. * @throws io::FormattingException */ std::vector Transformation::getTOWGS84Parameters() const // throw(io::FormattingException) { // GDAL WKT1 assumes EPSG:9606 / Position Vector convention bool sevenParamsTransform = false; bool threeParamsTransform = false; bool invertRotSigns = false; const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const int methodEPSGCode = l_method->getEPSGCode(); const auto paramCount = parameterValues().size(); if ((paramCount == 7 && ci_find(methodName, "Coordinate Frame") != std::string::npos) || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D) { sevenParamsTransform = true; invertRotSigns = true; } else if ((paramCount == 7 && ci_find(methodName, "Position Vector") != std::string::npos) || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D) { sevenParamsTransform = true; invertRotSigns = false; } else if ((paramCount == 3 && ci_find(methodName, "Geocentric translations") != std::string::npos) || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D) { threeParamsTransform = true; } if (threeParamsTransform || sevenParamsTransform) { std::vector params(7, 0.0); bool foundX = false; bool foundY = false; bool foundZ = false; bool foundRotX = false; bool foundRotY = false; bool foundRotZ = false; bool foundScale = false; const double rotSign = invertRotSigns ? -1.0 : 1.0; const auto fixNegativeZero = [](double x) { if (x == 0.0) return 0.0; return x; }; for (const auto &genOpParamvalue : parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (opParamvalue) { const auto ¶meter = opParamvalue->parameter(); const auto epsg_code = parameter->getEPSGCode(); const auto &l_parameterValue = opParamvalue->parameterValue(); if (l_parameterValue->type() == ParameterValue::Type::MEASURE) { const auto &measure = l_parameterValue->value(); if (epsg_code == EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION) { params[0] = measure.getSIValue(); foundX = true; } else if (epsg_code == EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION) { params[1] = measure.getSIValue(); foundY = true; } else if (epsg_code == EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION) { params[2] = measure.getSIValue(); foundZ = true; } else if (epsg_code == EPSG_CODE_PARAMETER_X_AXIS_ROTATION) { params[3] = fixNegativeZero( rotSign * measure.convertToUnit( common::UnitOfMeasure::ARC_SECOND)); foundRotX = true; } else if (epsg_code == EPSG_CODE_PARAMETER_Y_AXIS_ROTATION) { params[4] = fixNegativeZero( rotSign * measure.convertToUnit( common::UnitOfMeasure::ARC_SECOND)); foundRotY = true; } else if (epsg_code == EPSG_CODE_PARAMETER_Z_AXIS_ROTATION) { params[5] = fixNegativeZero( rotSign * measure.convertToUnit( common::UnitOfMeasure::ARC_SECOND)); foundRotZ = true; } else if (epsg_code == EPSG_CODE_PARAMETER_SCALE_DIFFERENCE) { params[6] = measure.convertToUnit( common::UnitOfMeasure::PARTS_PER_MILLION); foundScale = true; } } } } if (foundX && foundY && foundZ && (threeParamsTransform || (foundRotX && foundRotY && foundRotZ && foundScale))) { return params; } else { throw io::FormattingException( "Missing required parameter values in transformation"); } } #if 0 if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS || methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS) { auto offsetLat = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_OFFSET); auto offsetLong = parameterValueMeasure(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET); auto offsetHeight = parameterValueMeasure(EPSG_CODE_PARAMETER_VERTICAL_OFFSET); if (offsetLat.getSIValue() == 0.0 && offsetLong.getSIValue() == 0.0 && offsetHeight.getSIValue() == 0.0) { std::vector params(7, 0.0); return params; } } #endif throw io::FormattingException( "Transformation cannot be formatted as WKT1 TOWGS84 parameters"); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation from a vector of GeneralParameterValue. * * @param properties See \ref general_properties. At minimum the name should be * defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param interpolationCRSIn Interpolation CRS (might be null) * @param methodIn Operation method. * @param values Vector of GeneralOperationParameterNNPtr. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::create( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn, const OperationMethodNNPtr &methodIn, const std::vector &values, const std::vector &accuracies) { if (methodIn->parameters().size() != values.size()) { throw InvalidOperation( "Inconsistent number of parameters and parameter values"); } auto transf = Transformation::nn_make_shared( sourceCRSIn, targetCRSIn, interpolationCRSIn, methodIn, values, accuracies); transf->assignSelf(transf); transf->setProperties(properties); std::string name; if (properties.getStringValue(common::IdentifiedObject::NAME_KEY, name) && ci_find(name, "ballpark") != std::string::npos) { transf->setHasBallparkTransformation(true); } return transf; } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation ands its OperationMethod. * * @param propertiesTransformation The \ref general_properties of the * Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param interpolationCRSIn Interpolation CRS (might be null) * @param propertiesOperationMethod The \ref general_properties of the * OperationMethod. * At minimum the name should be defined. * @param parameters Vector of parameters of the operation method. * @param values Vector of ParameterValueNNPtr. Constraint: * values.size() == parameters.size() * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::create(const util::PropertyMap &propertiesTransformation, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn, const util::PropertyMap &propertiesOperationMethod, const std::vector ¶meters, const std::vector &values, const std::vector &accuracies) // throw InvalidOperation { OperationMethodNNPtr op( OperationMethod::create(propertiesOperationMethod, parameters)); if (parameters.size() != values.size()) { throw InvalidOperation( "Inconsistent number of parameters and parameter values"); } std::vector generalParameterValues; generalParameterValues.reserve(values.size()); for (size_t i = 0; i < values.size(); i++) { generalParameterValues.push_back( OperationParameterValue::create(parameters[i], values[i])); } return create(propertiesTransformation, sourceCRSIn, targetCRSIn, interpolationCRSIn, op, generalParameterValues, accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- static TransformationNNPtr createSevenParamsTransform( const util::PropertyMap &properties, const util::PropertyMap &methodProperties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, const std::vector &accuracies) { return Transformation::create( properties, sourceCRSIn, targetCRSIn, nullptr, methodProperties, VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE), }, createParams(common::Length(translationXMetre), common::Length(translationYMetre), common::Length(translationZMetre), common::Angle(rotationXArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Angle(rotationYArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Angle(rotationZArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Scale(scaleDifferencePPM, common::UnitOfMeasure::PARTS_PER_MILLION)), accuracies); } // --------------------------------------------------------------------------- static void getTransformationType(const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, bool &isGeocentric, bool &isGeog2D, bool &isGeog3D) { auto sourceCRSGeod = dynamic_cast(sourceCRSIn.get()); auto targetCRSGeod = dynamic_cast(targetCRSIn.get()); isGeocentric = sourceCRSGeod && sourceCRSGeod->isGeocentric() && targetCRSGeod && targetCRSGeod->isGeocentric(); if (isGeocentric) { isGeog2D = false; isGeog3D = false; return; } isGeocentric = false; auto sourceCRSGeog = dynamic_cast(sourceCRSIn.get()); auto targetCRSGeog = dynamic_cast(targetCRSIn.get()); if (!sourceCRSGeog || !targetCRSGeog) { throw InvalidOperation("Inconsistent CRS type"); } const auto nSrcAxisCount = sourceCRSGeog->coordinateSystem()->axisList().size(); const auto nTargetAxisCount = targetCRSGeog->coordinateSystem()->axisList().size(); isGeog2D = nSrcAxisCount == 2 && nTargetAxisCount == 2; isGeog3D = !isGeog2D && nSrcAxisCount >= 2 && nTargetAxisCount >= 2; } // --------------------------------------------------------------------------- static int useOperationMethodEPSGCodeIfPresent(const util::PropertyMap &properties, int nDefaultOperationMethodEPSGCode) { const auto *operationMethodEPSGCode = properties.get("OPERATION_METHOD_EPSG_CODE"); if (operationMethodEPSGCode) { const auto boxedValue = dynamic_cast( (*operationMethodEPSGCode).get()); if (boxedValue && boxedValue->type() == util::BoxedValue::Type::INTEGER) { return boxedValue->integerValue(); } } return nDefaultOperationMethodEPSGCode; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Geocentric Translations method. * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createGeocentricTranslations( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, const std::vector &accuracies) { bool isGeocentric; bool isGeog2D; bool isGeog3D; getTransformationType(sourceCRSIn, targetCRSIn, isGeocentric, isGeog2D, isGeog3D); return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( properties, isGeocentric ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D : EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D)), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION), }, createParams(common::Length(translationXMetre), common::Length(translationYMetre), common::Length(translationZMetre)), accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Position vector transformation * method. * * This is similar to createCoordinateFrameRotation(), except that the sign of * the rotation terms is inverted. * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param rotationXArcSecond Value of the Rotation_X parameter (in * arc-second). * @param rotationYArcSecond Value of the Rotation_Y parameter (in * arc-second). * @param rotationZArcSecond Value of the Rotation_Z parameter (in * arc-second). * @param scaleDifferencePPM Value of the Scale_Difference parameter (in * parts-per-million). * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createPositionVector( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, const std::vector &accuracies) { bool isGeocentric; bool isGeog2D; bool isGeog3D; getTransformationType(sourceCRSIn, targetCRSIn, isGeocentric, isGeog2D, isGeog3D); return createSevenParamsTransform( properties, createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( properties, isGeocentric ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D : EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Coordinate Frame Rotation method. * * This is similar to createPositionVector(), except that the sign of * the rotation terms is inverted. * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param rotationXArcSecond Value of the Rotation_X parameter (in * arc-second). * @param rotationYArcSecond Value of the Rotation_Y parameter (in * arc-second). * @param rotationZArcSecond Value of the Rotation_Z parameter (in * arc-second). * @param scaleDifferencePPM Value of the Scale_Difference parameter (in * parts-per-million). * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createCoordinateFrameRotation( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, const std::vector &accuracies) { bool isGeocentric; bool isGeog2D; bool isGeog3D; getTransformationType(sourceCRSIn, targetCRSIn, isGeocentric, isGeog2D, isGeog3D); return createSevenParamsTransform( properties, createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( properties, isGeocentric ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D : EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static TransformationNNPtr createFifteenParamsTransform( const util::PropertyMap &properties, const util::PropertyMap &methodProperties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, double rateTranslationX, double rateTranslationY, double rateTranslationZ, double rateRotationX, double rateRotationY, double rateRotationZ, double rateScaleDifference, double referenceEpochYear, const std::vector &accuracies) { return Transformation::create( properties, sourceCRSIn, targetCRSIn, nullptr, methodProperties, VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_X_AXIS_TRANSLATION), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Y_AXIS_TRANSLATION), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Z_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_RATE_X_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_RATE_Y_AXIS_ROTATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_RATE_Z_AXIS_ROTATION), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_SCALE_DIFFERENCE), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_REFERENCE_EPOCH), }, VectorOfValues{ common::Length(translationXMetre), common::Length(translationYMetre), common::Length(translationZMetre), common::Angle(rotationXArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Angle(rotationYArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Angle(rotationZArcSecond, common::UnitOfMeasure::ARC_SECOND), common::Scale(scaleDifferencePPM, common::UnitOfMeasure::PARTS_PER_MILLION), common::Measure(rateTranslationX, common::UnitOfMeasure::METRE_PER_YEAR), common::Measure(rateTranslationY, common::UnitOfMeasure::METRE_PER_YEAR), common::Measure(rateTranslationZ, common::UnitOfMeasure::METRE_PER_YEAR), common::Measure(rateRotationX, common::UnitOfMeasure::ARC_SECOND_PER_YEAR), common::Measure(rateRotationY, common::UnitOfMeasure::ARC_SECOND_PER_YEAR), common::Measure(rateRotationZ, common::UnitOfMeasure::ARC_SECOND_PER_YEAR), common::Measure(rateScaleDifference, common::UnitOfMeasure::PPM_PER_YEAR), common::Measure(referenceEpochYear, common::UnitOfMeasure::YEAR), }, accuracies); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Time Dependent position vector * transformation method. * * This is similar to createTimeDependentCoordinateFrameRotation(), except that * the sign of * the rotation terms is inverted. * * This method is defined as [EPSG:1053] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1053) * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param rotationXArcSecond Value of the Rotation_X parameter (in * arc-second). * @param rotationYArcSecond Value of the Rotation_Y parameter (in * arc-second). * @param rotationZArcSecond Value of the Rotation_Z parameter (in * arc-second). * @param scaleDifferencePPM Value of the Scale_Difference parameter (in * parts-per-million). * @param rateTranslationX Value of the rate of change of X-axis translation (in * metre/year) * @param rateTranslationY Value of the rate of change of Y-axis translation (in * metre/year) * @param rateTranslationZ Value of the rate of change of Z-axis translation (in * metre/year) * @param rateRotationX Value of the rate of change of X-axis rotation (in * arc-second/year) * @param rateRotationY Value of the rate of change of Y-axis rotation (in * arc-second/year) * @param rateRotationZ Value of the rate of change of Z-axis rotation (in * arc-second/year) * @param rateScaleDifference Value of the rate of change of scale difference * (in PPM/year) * @param referenceEpochYear Parameter reference epoch (in decimal year) * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createTimeDependentPositionVector( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, double rateTranslationX, double rateTranslationY, double rateTranslationZ, double rateRotationX, double rateRotationY, double rateRotationZ, double rateScaleDifference, double referenceEpochYear, const std::vector &accuracies) { bool isGeocentric; bool isGeog2D; bool isGeog3D; getTransformationType(sourceCRSIn, targetCRSIn, isGeocentric, isGeog2D, isGeog3D); return createFifteenParamsTransform( properties, createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D : EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, rateTranslationY, rateTranslationZ, rateRotationX, rateRotationY, rateRotationZ, rateScaleDifference, referenceEpochYear, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Time Dependent Position coordinate * frame rotation transformation method. * * This is similar to createTimeDependentPositionVector(), except that the sign * of * the rotation terms is inverted. * * This method is defined as [EPSG:1056] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1056) * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param rotationXArcSecond Value of the Rotation_X parameter (in * arc-second). * @param rotationYArcSecond Value of the Rotation_Y parameter (in * arc-second). * @param rotationZArcSecond Value of the Rotation_Z parameter (in * arc-second). * @param scaleDifferencePPM Value of the Scale_Difference parameter (in * parts-per-million). * @param rateTranslationX Value of the rate of change of X-axis translation (in * metre/year) * @param rateTranslationY Value of the rate of change of Y-axis translation (in * metre/year) * @param rateTranslationZ Value of the rate of change of Z-axis translation (in * metre/year) * @param rateRotationX Value of the rate of change of X-axis rotation (in * arc-second/year) * @param rateRotationY Value of the rate of change of Y-axis rotation (in * arc-second/year) * @param rateRotationZ Value of the rate of change of Z-axis rotation (in * arc-second/year) * @param rateScaleDifference Value of the rate of change of scale difference * (in PPM/year) * @param referenceEpochYear Parameter reference epoch (in decimal year) * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::createTimeDependentCoordinateFrameRotation( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double rotationXArcSecond, double rotationYArcSecond, double rotationZArcSecond, double scaleDifferencePPM, double rateTranslationX, double rateTranslationY, double rateTranslationZ, double rateRotationX, double rateRotationY, double rateRotationZ, double rateScaleDifference, double referenceEpochYear, const std::vector &accuracies) { bool isGeocentric; bool isGeog2D; bool isGeog3D; getTransformationType(sourceCRSIn, targetCRSIn, isGeocentric, isGeog2D, isGeog3D); return createFifteenParamsTransform( properties, createMethodMapNameEPSGCode(useOperationMethodEPSGCodeIfPresent( properties, isGeocentric ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC : isGeog2D ? EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D : EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D)), sourceCRSIn, targetCRSIn, translationXMetre, translationYMetre, translationZMetre, rotationXArcSecond, rotationYArcSecond, rotationZArcSecond, scaleDifferencePPM, rateTranslationX, rateTranslationY, rateTranslationZ, rateRotationX, rateRotationY, rateRotationZ, rateScaleDifference, referenceEpochYear, accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static TransformationNNPtr _createMolodensky( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, int methodEPSGCode, double translationXMetre, double translationYMetre, double translationZMetre, double semiMajorAxisDifferenceMetre, double flattingDifference, const std::vector &accuracies) { return Transformation::create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(methodEPSGCode), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_SEMI_MAJOR_AXIS_DIFFERENCE), createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_FLATTENING_DIFFERENCE), }, createParams( common::Length(translationXMetre), common::Length(translationYMetre), common::Length(translationZMetre), common::Length(semiMajorAxisDifferenceMetre), common::Measure(flattingDifference, common::UnitOfMeasure::NONE)), accuracies); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Molodensky method. * * @see createAbridgedMolodensky() for a related method. * * This method is defined as [EPSG:9604] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9604) * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param semiMajorAxisDifferenceMetre The difference between the semi-major * axis values of the ellipsoids used in the target and source CRS (in metre). * @param flattingDifference The difference between the flattening values of * the ellipsoids used in the target and source CRS. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::createMolodensky( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double semiMajorAxisDifferenceMetre, double flattingDifference, const std::vector &accuracies) { return _createMolodensky( properties, sourceCRSIn, targetCRSIn, EPSG_CODE_METHOD_MOLODENSKY, translationXMetre, translationYMetre, translationZMetre, semiMajorAxisDifferenceMetre, flattingDifference, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with Abridged Molodensky method. * * @see createdMolodensky() for a related method. * * This method is defined as [EPSG:9605] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9605) * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param translationXMetre Value of the Translation_X parameter (in metre). * @param translationYMetre Value of the Translation_Y parameter (in metre). * @param translationZMetre Value of the Translation_Z parameter (in metre). * @param semiMajorAxisDifferenceMetre The difference between the semi-major * axis values of the ellipsoids used in the target and source CRS (in metre). * @param flattingDifference The difference between the flattening values of * the ellipsoids used in the target and source CRS. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::createAbridgedMolodensky( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, double translationXMetre, double translationYMetre, double translationZMetre, double semiMajorAxisDifferenceMetre, double flattingDifference, const std::vector &accuracies) { return _createMolodensky(properties, sourceCRSIn, targetCRSIn, EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY, translationXMetre, translationYMetre, translationZMetre, semiMajorAxisDifferenceMetre, flattingDifference, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation from TOWGS84 parameters. * * This is a helper of createPositionVector() with the source CRS being the * GeographicCRS of sourceCRSIn, and the target CRS being EPSG:4326 * * @param sourceCRSIn Source CRS. * @param TOWGS84Parameters The vector of 3 double values (Translation_X,_Y,_Z) * or 7 double values (Translation_X,_Y,_Z, Rotation_X,_Y,_Z, Scale_Difference) * passed to createPositionVector() * @return new Transformation. * @throws InvalidOperation */ TransformationNNPtr Transformation::createTOWGS84( const crs::CRSNNPtr &sourceCRSIn, const std::vector &TOWGS84Parameters) // throw InvalidOperation { if (TOWGS84Parameters.size() != 3 && TOWGS84Parameters.size() != 7) { throw InvalidOperation( "Invalid number of elements in TOWGS84Parameters"); } crs::CRSPtr transformSourceCRS = sourceCRSIn->extractGeodeticCRS(); if (!transformSourceCRS) { throw InvalidOperation( "Cannot find GeodeticCRS in sourceCRS of TOWGS84 transformation"); } util::PropertyMap properties; properties.set(common::IdentifiedObject::NAME_KEY, concat("Transformation from ", transformSourceCRS->nameStr(), " to WGS84")); auto targetCRS = dynamic_cast(transformSourceCRS.get()) ? util::nn_static_pointer_cast( crs::GeographicCRS::EPSG_4326) : util::nn_static_pointer_cast( crs::GeodeticCRS::EPSG_4978); if (TOWGS84Parameters.size() == 3) { return createGeocentricTranslations( properties, NN_NO_CHECK(transformSourceCRS), targetCRS, TOWGS84Parameters[0], TOWGS84Parameters[1], TOWGS84Parameters[2], {}); } return createPositionVector(properties, NN_NO_CHECK(transformSourceCRS), targetCRS, TOWGS84Parameters[0], TOWGS84Parameters[1], TOWGS84Parameters[2], TOWGS84Parameters[3], TOWGS84Parameters[4], TOWGS84Parameters[5], TOWGS84Parameters[6], {}); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with NTv2 method. * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param filename NTv2 filename. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createNTv2( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const std::string &filename, const std::vector &accuracies) { return create(properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_NTV2), VectorOfParameters{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)}, VectorOfValues{ParameterValue::createFilename(filename)}, accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static TransformationNNPtr _createGravityRelatedHeightToGeographic3D( const util::PropertyMap &properties, bool inverse, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn, const std::string &filename, const std::vector &accuracies) { return Transformation::create( properties, sourceCRSIn, targetCRSIn, interpolationCRSIn, util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, inverse ? INVERSE_OF + PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D : PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D), VectorOfParameters{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME)}, VectorOfValues{ParameterValue::createFilename(filename)}, accuracies); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation from GravityRelatedHeight to * Geographic3D * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param interpolationCRSIn Interpolation CRS. (might be null) * @param filename GRID filename. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createGravityRelatedHeightToGeographic3D( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn, const std::string &filename, const std::vector &accuracies) { return _createGravityRelatedHeightToGeographic3D( properties, false, sourceCRSIn, targetCRSIn, interpolationCRSIn, filename, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with method VERTCON * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param filename GRID filename. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createVERTCON( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const std::string &filename, const std::vector &accuracies) { return create(properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_VERTCON), VectorOfParameters{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE)}, VectorOfValues{ParameterValue::createFilename(filename)}, accuracies); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static inline std::vector buildAccuracyZero() { return std::vector{ metadata::PositionalAccuracy::create("0")}; } // --------------------------------------------------------------------------- //! @endcond /** \brief Instantiate a transformation with method Longitude rotation * * This method is defined as [EPSG:9601] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9601) * * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param offset Longitude offset to add. * @return new Transformation. */ TransformationNNPtr Transformation::createLongitudeRotation( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offset) { return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_LONGITUDE_ROTATION), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET)}, VectorOfValues{ParameterValue::create(offset)}, buildAccuracyZero()); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool Transformation::isLongitudeRotation() const { return method()->getEPSGCode() == EPSG_CODE_METHOD_LONGITUDE_ROTATION; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with method Geographic 2D offsets * * This method is defined as [EPSG:9619] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9619) * * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param offsetLat Latitude offset to add. * @param offsetLon Longitude offset to add. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createGeographic2DOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, const common::Angle &offsetLon, const std::vector &accuracies) { return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LATITUDE_OFFSET), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET)}, VectorOfValues{offsetLat, offsetLon}, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with method Geographic 3D offsets * * This method is defined as [EPSG:9660] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9660) * * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param offsetLat Latitude offset to add. * @param offsetLon Longitude offset to add. * @param offsetHeight Height offset to add. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createGeographic3DOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, const common::Angle &offsetLon, const common::Length &offsetHeight, const std::vector &accuracies) { return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LATITUDE_OFFSET), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_VERTICAL_OFFSET)}, VectorOfValues{offsetLat, offsetLon, offsetHeight}, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with method Geographic 2D with * height * offsets * * This method is defined as [EPSG:9618] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9618) * * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param offsetLat Latitude offset to add. * @param offsetLon Longitude offset to add. * @param offsetHeight Geoid undulation to add. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createGeographic2DWithHeightOffsets( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Angle &offsetLat, const common::Angle &offsetLon, const common::Length &offsetHeight, const std::vector &accuracies) { return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode( EPSG_CODE_METHOD_GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS), VectorOfParameters{ createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LATITUDE_OFFSET), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET), createOpParamNameEPSGCode(EPSG_CODE_PARAMETER_GEOID_UNDULATION)}, VectorOfValues{offsetLat, offsetLon, offsetHeight}, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation with method Vertical Offset. * * This method is defined as [EPSG:9616] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::9616) * * * @param properties See \ref general_properties of the Transformation. * At minimum the name should be defined. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param offsetHeight Geoid undulation to add. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. */ TransformationNNPtr Transformation::createVerticalOffset( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Length &offsetHeight, const std::vector &accuracies) { return create(properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_VERTICAL_OFFSET), VectorOfParameters{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_VERTICAL_OFFSET)}, VectorOfValues{offsetHeight}, accuracies); } // --------------------------------------------------------------------------- /** \brief Instantiate a transformation based on the Change of Vertical Unit * method. * * This method is defined as [EPSG:1069] * (https://www.epsg-registry.org/export.htm?gml=urn:ogc:def:method:EPSG::1069) * * @param properties See \ref general_properties of the conversion. If the name * is not provided, it is automatically set. * @param sourceCRSIn Source CRS. * @param targetCRSIn Target CRS. * @param factor Conversion factor * @param accuracies Vector of positional accuracy (might be empty). * @return a new Transformation. */ TransformationNNPtr Transformation::createChangeVerticalUnit( const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const common::Scale &factor, const std::vector &accuracies) { return create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT), VectorOfParameters{ createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR), }, VectorOfValues{ factor, }, accuracies); } // --------------------------------------------------------------------------- static util::PropertyMap createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom, bool approximateInversion) { assert(op); util::PropertyMap map; // The domain(s) are unchanged by the inverse operation addDomains(map, op); const std::string &forwardName = op->nameStr(); // Forge a name for the inverse, either from the forward name, or // from the source and target CRS names const char *opType; if (starts_with(forwardName, BALLPARK_GEOCENTRIC_TRANSLATION)) { opType = BALLPARK_GEOCENTRIC_TRANSLATION; } else if (starts_with(forwardName, BALLPARK_GEOGRAPHIC_OFFSET)) { opType = BALLPARK_GEOGRAPHIC_OFFSET; } else if (starts_with(forwardName, NULL_GEOGRAPHIC_OFFSET)) { opType = NULL_GEOGRAPHIC_OFFSET; } else if (starts_with(forwardName, NULL_GEOCENTRIC_TRANSLATION)) { opType = NULL_GEOCENTRIC_TRANSLATION; } else if (dynamic_cast(op) || starts_with(forwardName, "Transformation from ")) { opType = "Transformation"; } else if (dynamic_cast(op)) { opType = "Conversion"; } else { opType = "Operation"; } auto sourceCRS = op->sourceCRS(); auto targetCRS = op->targetCRS(); std::string name; if (!forwardName.empty()) { if (starts_with(forwardName, INVERSE_OF) || forwardName.find(" + ") != std::string::npos) { auto tokens = split(forwardName, " + "); for (size_t i = tokens.size(); i > 0;) { i--; if (!name.empty()) { name += " + "; } if (starts_with(tokens[i], INVERSE_OF)) { name += tokens[i].substr(INVERSE_OF.size()); } else if (tokens[i] == AXIS_ORDER_CHANGE_2D_NAME) { name += tokens[i]; } else { name += INVERSE_OF + tokens[i]; } } } else if (!sourceCRS || !targetCRS || forwardName != buildOpName(opType, sourceCRS, targetCRS)) { name = INVERSE_OF + forwardName; } } if (name.empty() && sourceCRS && targetCRS) { name = buildOpName(opType, targetCRS, sourceCRS); } if (approximateInversion) { name += " (approx. inversion)"; } if (!name.empty()) { map.set(common::IdentifiedObject::NAME_KEY, name); } const std::string &remarks = op->remarks(); if (!remarks.empty()) { map.set(common::IdentifiedObject::REMARKS_KEY, remarks); } addModifiedIdentifier(map, op, true, derivedFrom); const auto so = dynamic_cast(op); if (so) { const int soMethodEPSGCode = so->method()->getEPSGCode(); if (soMethodEPSGCode > 0) { map.set("OPERATION_METHOD_EPSG_CODE", soMethodEPSGCode); } } return map; } // --------------------------------------------------------------------------- static bool isTimeDependent(const std::string &methodName) { return ci_find(methodName, "Time dependent") != std::string::npos || ci_find(methodName, "Time-dependent") != std::string::npos; } // --------------------------------------------------------------------------- // to avoid -0... static double negate(double val) { if (val != 0) { return -val; } return 0.0; } // --------------------------------------------------------------------------- static CoordinateOperationPtr createApproximateInverseIfPossible(const Transformation *op) { bool sevenParamsTransform = false; bool fifteenParamsTransform = false; const auto &method = op->method(); const auto &methodName = method->nameStr(); const int methodEPSGCode = method->getEPSGCode(); const auto paramCount = op->parameterValues().size(); const bool isPositionVector = ci_find(methodName, "Position Vector") != std::string::npos; const bool isCoordinateFrame = ci_find(methodName, "Coordinate Frame") != std::string::npos; // See end of "2.4.3.3 Helmert 7-parameter transformations" // in EPSG 7-2 guidance // For practical purposes, the inverse of 7- or 15-parameters Helmert // can be obtained by using the forward method with all parameters // negated // (except reference epoch!) // So for WKT export use that. But for PROJ string, we use the +inv flag // so as to get "perfect" round-tripability. if ((paramCount == 7 && isCoordinateFrame && !isTimeDependent(methodName)) || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D) { sevenParamsTransform = true; } else if ( (paramCount == 15 && isCoordinateFrame && isTimeDependent(methodName)) || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D) { fifteenParamsTransform = true; } else if ((paramCount == 7 && isPositionVector && !isTimeDependent(methodName)) || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D) { sevenParamsTransform = true; } else if ( (paramCount == 15 && isPositionVector && isTimeDependent(methodName)) || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D) { fifteenParamsTransform = true; } if (sevenParamsTransform || fifteenParamsTransform) { double neg_x = negate(op->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION)); double neg_y = negate(op->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION)); double neg_z = negate(op->parameterValueNumericAsSI( EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION)); double neg_rx = negate( op->parameterValueNumeric(EPSG_CODE_PARAMETER_X_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND)); double neg_ry = negate( op->parameterValueNumeric(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND)); double neg_rz = negate( op->parameterValueNumeric(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND)); double neg_scaleDiff = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_SCALE_DIFFERENCE, common::UnitOfMeasure::PARTS_PER_MILLION)); auto methodProperties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, methodName); int method_epsg_code = method->getEPSGCode(); if (method_epsg_code) { methodProperties .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, method_epsg_code); } if (fifteenParamsTransform) { double neg_rate_x = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_X_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR)); double neg_rate_y = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Y_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR)); double neg_rate_z = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Z_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR)); double neg_rate_rx = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_X_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR)); double neg_rate_ry = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Y_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR)); double neg_rate_rz = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Z_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR)); double neg_rate_scaleDiff = negate(op->parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_SCALE_DIFFERENCE, common::UnitOfMeasure::PPM_PER_YEAR)); double referenceEpochYear = op->parameterValueNumeric(EPSG_CODE_PARAMETER_REFERENCE_EPOCH, common::UnitOfMeasure::YEAR); return util::nn_static_pointer_cast( createFifteenParamsTransform( createPropertiesForInverse(op, false, true), methodProperties, op->targetCRS(), op->sourceCRS(), neg_x, neg_y, neg_z, neg_rx, neg_ry, neg_rz, neg_scaleDiff, neg_rate_x, neg_rate_y, neg_rate_z, neg_rate_rx, neg_rate_ry, neg_rate_rz, neg_rate_scaleDiff, referenceEpochYear, op->coordinateOperationAccuracies())) .as_nullable(); } else { return util::nn_static_pointer_cast( createSevenParamsTransform( createPropertiesForInverse(op, false, true), methodProperties, op->targetCRS(), op->sourceCRS(), neg_x, neg_y, neg_z, neg_rx, neg_ry, neg_rz, neg_scaleDiff, op->coordinateOperationAccuracies())) .as_nullable(); } } return nullptr; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress TransformationNNPtr Transformation::Private::registerInv(util::BaseObjectNNPtr thisIn, TransformationNNPtr invTransform) { invTransform->d->forwardOperation_ = util::nn_dynamic_pointer_cast(thisIn); invTransform->setHasBallparkTransformation( invTransform->d->forwardOperation_->hasBallparkTransformation()); return invTransform; } //! @endcond // --------------------------------------------------------------------------- CoordinateOperationNNPtr Transformation::inverse() const { return inverseAsTransformation(); } // --------------------------------------------------------------------------- TransformationNNPtr Transformation::inverseAsTransformation() const { if (d->forwardOperation_) { return NN_NO_CHECK(d->forwardOperation_); } const auto &l_method = method(); const auto &methodName = l_method->nameStr(); const int methodEPSGCode = l_method->getEPSGCode(); const auto &l_sourceCRS = sourceCRS(); const auto &l_targetCRS = targetCRS(); // For geocentric translation, the inverse is exactly the negation of // the parameters. if (ci_find(methodName, "Geocentric translations") != std::string::npos || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D) { double x = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION); double y = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION); double z = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION); return d->registerInv( shared_from_this(), createGeocentricTranslations( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, negate(x), negate(y), negate(z), coordinateOperationAccuracies())); } if (methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY || methodEPSGCode == EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY) { double x = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION); double y = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION); double z = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION); double da = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SEMI_MAJOR_AXIS_DIFFERENCE); double df = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_FLATTENING_DIFFERENCE); if (methodEPSGCode == EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY) { return d->registerInv( shared_from_this(), createAbridgedMolodensky( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, negate(x), negate(y), negate(z), negate(da), negate(df), coordinateOperationAccuracies())); } else { return d->registerInv( shared_from_this(), createMolodensky(createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, negate(x), negate(y), negate(z), negate(da), negate(df), coordinateOperationAccuracies())); } } if (isLongitudeRotation()) { auto offset = parameterValueMeasure(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET); const common::Angle newOffset(negate(offset.value()), offset.unit()); return d->registerInv( shared_from_this(), createLongitudeRotation( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, newOffset)); } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS) { auto offsetLat = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_OFFSET); const common::Angle newOffsetLat(negate(offsetLat.value()), offsetLat.unit()); auto offsetLong = parameterValueMeasure(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET); const common::Angle newOffsetLong(negate(offsetLong.value()), offsetLong.unit()); return d->registerInv( shared_from_this(), createGeographic2DOffsets( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, newOffsetLat, newOffsetLong, coordinateOperationAccuracies())); } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS) { auto offsetLat = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_OFFSET); const common::Angle newOffsetLat(negate(offsetLat.value()), offsetLat.unit()); auto offsetLong = parameterValueMeasure(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET); const common::Angle newOffsetLong(negate(offsetLong.value()), offsetLong.unit()); auto offsetHeight = parameterValueMeasure(EPSG_CODE_PARAMETER_VERTICAL_OFFSET); const common::Length newOffsetHeight(negate(offsetHeight.value()), offsetHeight.unit()); return d->registerInv( shared_from_this(), createGeographic3DOffsets( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, newOffsetLat, newOffsetLong, newOffsetHeight, coordinateOperationAccuracies())); } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS) { auto offsetLat = parameterValueMeasure(EPSG_CODE_PARAMETER_LATITUDE_OFFSET); const common::Angle newOffsetLat(negate(offsetLat.value()), offsetLat.unit()); auto offsetLong = parameterValueMeasure(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET); const common::Angle newOffsetLong(negate(offsetLong.value()), offsetLong.unit()); auto offsetHeight = parameterValueMeasure(EPSG_CODE_PARAMETER_GEOID_UNDULATION); const common::Length newOffsetHeight(negate(offsetHeight.value()), offsetHeight.unit()); return d->registerInv( shared_from_this(), createGeographic2DWithHeightOffsets( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, newOffsetLat, newOffsetLong, newOffsetHeight, coordinateOperationAccuracies())); } if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_OFFSET) { auto offsetHeight = parameterValueMeasure(EPSG_CODE_PARAMETER_VERTICAL_OFFSET); const common::Length newOffsetHeight(negate(offsetHeight.value()), offsetHeight.unit()); return d->registerInv( shared_from_this(), createVerticalOffset(createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, newOffsetHeight, coordinateOperationAccuracies())); } if (methodEPSGCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT) { const double convFactor = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR); return d->registerInv( shared_from_this(), createChangeVerticalUnit( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, common::Scale(1.0 / convFactor), coordinateOperationAccuracies())); } #ifdef notdef // We don't need that currently, but we might... if (methodEPSGCode == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) { return d->registerInv( shared_from_this(), createHeightDepthReversal( createPropertiesForInverse(this, false, false), l_targetCRS, l_sourceCRS, coordinateOperationAccuracies())); } #endif return InverseTransformation::create(NN_NO_CHECK( util::nn_dynamic_pointer_cast(shared_from_this()))); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- InverseTransformation::InverseTransformation(const TransformationNNPtr &forward) : Transformation( forward->targetCRS(), forward->sourceCRS(), forward->interpolationCRS(), OperationMethod::create(createPropertiesForInverse(forward->method()), forward->method()->parameters()), forward->parameterValues(), forward->coordinateOperationAccuracies()), InverseCoordinateOperation(forward, true) { setPropertiesFromForward(); } // --------------------------------------------------------------------------- InverseTransformation::~InverseTransformation() = default; // --------------------------------------------------------------------------- TransformationNNPtr InverseTransformation::create(const TransformationNNPtr &forward) { auto conv = util::nn_make_shared(forward); conv->assignSelf(conv); return conv; } // --------------------------------------------------------------------------- TransformationNNPtr InverseTransformation::inverseAsTransformation() const { return NN_NO_CHECK( util::nn_dynamic_pointer_cast(forwardOperation_)); } // --------------------------------------------------------------------------- void InverseTransformation::_exportToWKT(io::WKTFormatter *formatter) const { auto approxInverse = createApproximateInverseIfPossible( util::nn_dynamic_pointer_cast(forwardOperation_).get()); if (approxInverse) { approxInverse->_exportToWKT(formatter); } else { Transformation::_exportToWKT(formatter); } } // --------------------------------------------------------------------------- CoordinateOperationNNPtr InverseTransformation::_shallowClone() const { auto op = InverseTransformation::nn_make_shared( inverseAsTransformation()->shallowClone()); op->assignSelf(op); op->setCRSs(this, false); return util::nn_static_pointer_cast(op); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Transformation::_exportToWKT(io::WKTFormatter *formatter) const { exportTransformationToWKT(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void Transformation::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext( formatter->abridgedTransformation() ? "AbridgedTransformation" : "Transformation", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } if (!formatter->abridgedTransformation()) { writer.AddObjKey("source_crs"); formatter->setAllowIDInImmediateChild(); sourceCRS()->_exportToJSON(formatter); writer.AddObjKey("target_crs"); formatter->setAllowIDInImmediateChild(); targetCRS()->_exportToJSON(formatter); const auto &l_interpolationCRS = interpolationCRS(); if (l_interpolationCRS) { writer.AddObjKey("interpolation_crs"); formatter->setAllowIDInImmediateChild(); l_interpolationCRS->_exportToJSON(formatter); } } writer.AddObjKey("method"); formatter->setOmitTypeInImmediateChild(); formatter->setAllowIDInImmediateChild(); method()->_exportToJSON(formatter); writer.AddObjKey("parameters"); { auto parametersContext(writer.MakeArrayContext(false)); for (const auto &genOpParamvalue : parameterValues()) { formatter->setAllowIDInImmediateChild(); formatter->setOmitTypeInImmediateChild(); genOpParamvalue->_exportToJSON(formatter); } } if (!formatter->abridgedTransformation()) { if (!coordinateOperationAccuracies().empty()) { writer.AddObjKey("accuracy"); writer.Add(coordinateOperationAccuracies()[0]->value()); } } if (formatter->abridgedTransformation()) { if (formatter->outputId()) { formatID(formatter); } } else { ObjectUsage::baseExportToJSON(formatter); } } //! @endcond // --------------------------------------------------------------------------- static void exportSourceCRSAndTargetCRSToWKT(const CoordinateOperation *co, io::WKTFormatter *formatter) { auto l_sourceCRS = co->sourceCRS(); assert(l_sourceCRS); auto l_targetCRS = co->targetCRS(); assert(l_targetCRS); const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; const bool canExportCRSId = (isWKT2 && formatter->use2019Keywords() && !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())); const bool hasDomains = !co->domains().empty(); if (hasDomains) { formatter->pushDisableUsage(); } formatter->startNode(io::WKTConstants::SOURCECRS, false); if (canExportCRSId && !l_sourceCRS->identifiers().empty()) { // fake that top node has no id, so that the sourceCRS id is // considered formatter->pushHasId(false); l_sourceCRS->_exportToWKT(formatter); formatter->popHasId(); } else { l_sourceCRS->_exportToWKT(formatter); } formatter->endNode(); formatter->startNode(io::WKTConstants::TARGETCRS, false); if (canExportCRSId && !l_targetCRS->identifiers().empty()) { // fake that top node has no id, so that the targetCRS id is // considered formatter->pushHasId(false); l_targetCRS->_exportToWKT(formatter); formatter->popHasId(); } else { l_targetCRS->_exportToWKT(formatter); } formatter->endNode(); if (hasDomains) { formatter->popDisableUsage(); } } // --------------------------------------------------------------------------- void SingleOperation::exportTransformationToWKT( io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { throw io::FormattingException( "Transformation can only be exported to WKT2"); } if (formatter->abridgedTransformation()) { formatter->startNode(io::WKTConstants::ABRIDGEDTRANSFORMATION, !identifiers().empty()); } else { formatter->startNode(io::WKTConstants::COORDINATEOPERATION, !identifiers().empty()); } formatter->addQuotedString(nameStr()); if (formatter->use2019Keywords()) { const auto &version = operationVersion(); if (version.has_value()) { formatter->startNode(io::WKTConstants::VERSION, false); formatter->addQuotedString(*version); formatter->endNode(); } } if (!formatter->abridgedTransformation()) { exportSourceCRSAndTargetCRSToWKT(this, formatter); } method()->_exportToWKT(formatter); for (const auto ¶mValue : parameterValues()) { paramValue->_exportToWKT(formatter, nullptr); } if (!formatter->abridgedTransformation()) { if (interpolationCRS()) { formatter->startNode(io::WKTConstants::INTERPOLATIONCRS, false); interpolationCRS()->_exportToWKT(formatter); formatter->endNode(); } if (!coordinateOperationAccuracies().empty()) { formatter->startNode(io::WKTConstants::OPERATIONACCURACY, false); formatter->add(coordinateOperationAccuracies()[0]->value()); formatter->endNode(); } } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string nullString; static const std::string &_getNTv2Filename(const Transformation *op, bool allowInverse) { const auto &l_method = op->method(); if (l_method->getEPSGCode() == EPSG_CODE_METHOD_NTV2 || (allowInverse && ci_equal(l_method->nameStr(), INVERSE_OF + EPSG_NAME_METHOD_NTV2))) { const auto &fileParameter = op->parameterValue( EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE, EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { return fileParameter->valueFile(); } } return nullString; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const std::string &Transformation::getNTv2Filename() const { return _getNTv2Filename(this, false); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string &_getNTv1Filename(const Transformation *op, bool allowInverse) { const auto &l_method = op->method(); const auto &methodName = l_method->nameStr(); if (l_method->getEPSGCode() == EPSG_CODE_METHOD_NTV1 || (allowInverse && ci_equal(methodName, INVERSE_OF + EPSG_NAME_METHOD_NTV1))) { const auto &fileParameter = op->parameterValue( EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE, EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { return fileParameter->valueFile(); } } return nullString; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string &_getCTABLE2Filename(const Transformation *op, bool allowInverse) { const auto &l_method = op->method(); const auto &methodName = l_method->nameStr(); if (ci_equal(methodName, PROJ_WKT2_NAME_METHOD_CTABLE2) || (allowInverse && ci_equal(methodName, INVERSE_OF + PROJ_WKT2_NAME_METHOD_CTABLE2))) { const auto &fileParameter = op->parameterValue( EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE, EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { return fileParameter->valueFile(); } } return nullString; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static const std::string & _getHeightToGeographic3DFilename(const Transformation *op, bool allowInverse) { const auto &methodName = op->method()->nameStr(); if (ci_equal(methodName, PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D) || (allowInverse && ci_equal(methodName, INVERSE_OF + PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D))) { const auto &fileParameter = op->parameterValue(EPSG_NAME_PARAMETER_GEOID_CORRECTION_FILENAME, EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { return fileParameter->valueFile(); } } return nullString; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress const std::string &Transformation::getHeightToGeographic3DFilename() const { return _getHeightToGeographic3DFilename(this, false); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool isGeographic3DToGravityRelatedHeight(const OperationMethodNNPtr &method, bool allowInverse) { const auto &methodName = method->nameStr(); static const char *const methodCodes[] = { "1025", // Geographic3D to GravityRelatedHeight (EGM2008) "1030", // Geographic3D to GravityRelatedHeight (NZgeoid) "1045", // Geographic3D to GravityRelatedHeight (OSGM02-Ire) "1047", // Geographic3D to GravityRelatedHeight (Gravsoft) "1048", // Geographic3D to GravityRelatedHeight (Ausgeoid v2) "1050", // Geographic3D to GravityRelatedHeight (CI) "1059", // Geographic3D to GravityRelatedHeight (PNG) "1060", // Geographic3D to GravityRelatedHeight (CGG2013) "1072", // Geographic3D to GravityRelatedHeight (OSGM15-Ire) "1073", // Geographic3D to GravityRelatedHeight (IGN2009) "1081", // Geographic3D to GravityRelatedHeight (BEV AT) "9661", // Geographic3D to GravityRelatedHeight (EGM) "9662", // Geographic3D to GravityRelatedHeight (Ausgeoid98) "9663", // Geographic3D to GravityRelatedHeight (OSGM-GB) "9664", // Geographic3D to GravityRelatedHeight (IGN1997) "9665", // Geographic3D to GravityRelatedHeight (US .gtx) }; if (ci_find(methodName, "Geographic3D to GravityRelatedHeight") == 0) { return true; } if (allowInverse && ci_find(methodName, INVERSE_OF + "Geographic3D to GravityRelatedHeight") == 0) { return true; } for (const auto &code : methodCodes) { for (const auto &idSrc : method->identifiers()) { const auto &srcAuthName = *(idSrc->codeSpace()); const auto &srcCode = idSrc->code(); if (ci_equal(srcAuthName, "EPSG") && srcCode == code) { return true; } if (allowInverse && ci_equal(srcAuthName, "INVERSE(EPSG)") && srcCode == code) { return true; } } } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createSimilarPropertiesMethod(common::IdentifiedObjectNNPtr obj) { util::PropertyMap map; const std::string &forwardName = obj->nameStr(); if (!forwardName.empty()) { map.set(common::IdentifiedObject::NAME_KEY, forwardName); } { auto ar = util::ArrayOfBaseObject::create(); for (const auto &idSrc : obj->identifiers()) { const auto &srcAuthName = *(idSrc->codeSpace()); const auto &srcCode = idSrc->code(); auto idsProp = util::PropertyMap().set( metadata::Identifier::CODESPACE_KEY, srcAuthName); ar->add(metadata::Identifier::create(srcCode, idsProp)); } if (!ar->empty()) { map.set(common::IdentifiedObject::IDENTIFIERS_KEY, ar); } } return map; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createSimilarPropertiesTransformation(TransformationNNPtr obj) { util::PropertyMap map; // The domain(s) are unchanged addDomains(map, obj.get()); std::string forwardName = obj->nameStr(); if (!forwardName.empty()) { map.set(common::IdentifiedObject::NAME_KEY, forwardName); } addModifiedIdentifier(map, obj.get(), false, true); return map; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static TransformationNNPtr createNTv1(const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn, const std::string &filename, const std::vector &accuracies) { return Transformation::create( properties, sourceCRSIn, targetCRSIn, nullptr, createMethodMapNameEPSGCode(EPSG_CODE_METHOD_NTV1), {OperationParameter::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, EPSG_NAME_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE))}, {ParameterValue::createFilename(filename)}, accuracies); } //! @endcond // --------------------------------------------------------------------------- /** \brief Return an equivalent transformation to the current one, but using * PROJ alternative grid names. */ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames( io::DatabaseContextNNPtr databaseContext) const { auto self = NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); const auto &l_method = method(); const int methodEPSGCode = l_method->getEPSGCode(); std::string projFilename; std::string projGridFormat; bool inverseDirection = false; const auto &NTv1Filename = _getNTv1Filename(this, false); const auto &NTv2Filename = _getNTv2Filename(this, false); std::string lasFilename; if (methodEPSGCode == EPSG_CODE_METHOD_NADCON) { const auto &latitudeFileParameter = parameterValue(EPSG_NAME_PARAMETER_LATITUDE_DIFFERENCE_FILE, EPSG_CODE_PARAMETER_LATITUDE_DIFFERENCE_FILE); const auto &longitudeFileParameter = parameterValue(EPSG_NAME_PARAMETER_LONGITUDE_DIFFERENCE_FILE, EPSG_CODE_PARAMETER_LONGITUDE_DIFFERENCE_FILE); if (latitudeFileParameter && latitudeFileParameter->type() == ParameterValue::Type::FILENAME && longitudeFileParameter && longitudeFileParameter->type() == ParameterValue::Type::FILENAME) { lasFilename = latitudeFileParameter->valueFile(); } } const auto &horizontalGridName = !NTv1Filename.empty() ? NTv1Filename : !NTv2Filename.empty() ? NTv2Filename : lasFilename; if (!horizontalGridName.empty() && databaseContext->lookForGridAlternative(horizontalGridName, projFilename, projGridFormat, inverseDirection)) { if (horizontalGridName == projFilename) { if (inverseDirection) { throw util::UnsupportedOperationException( "Inverse direction for " + projFilename + " not supported"); } return self; } const auto &l_sourceCRS = sourceCRS(); const auto &l_targetCRS = targetCRS(); const auto &l_accuracies = coordinateOperationAccuracies(); if (projGridFormat == "NTv1") { if (inverseDirection) { return createNTv1(createPropertiesForInverse( self.as_nullable().get(), true, false), l_targetCRS, l_sourceCRS, projFilename, l_accuracies) ->inverseAsTransformation(); } else { return createNTv1(createSimilarPropertiesTransformation(self), l_sourceCRS, l_targetCRS, projFilename, l_accuracies); } } else if (projGridFormat == "NTv2") { if (inverseDirection) { return createNTv2(createPropertiesForInverse( self.as_nullable().get(), true, false), l_targetCRS, l_sourceCRS, projFilename, l_accuracies) ->inverseAsTransformation(); } else { return createNTv2(createSimilarPropertiesTransformation(self), l_sourceCRS, l_targetCRS, projFilename, l_accuracies); } } else if (projGridFormat == "CTable2") { auto parameters = std::vector{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_LATITUDE_LONGITUDE_DIFFERENCE_FILE)}; auto methodProperties = util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, PROJ_WKT2_NAME_METHOD_CTABLE2); auto values = std::vector{ ParameterValue::createFilename(projFilename)}; if (inverseDirection) { return create(createPropertiesForInverse( self.as_nullable().get(), true, false), l_targetCRS, l_sourceCRS, nullptr, methodProperties, parameters, values, l_accuracies) ->inverseAsTransformation(); } else { return create(createSimilarPropertiesTransformation(self), l_sourceCRS, l_targetCRS, nullptr, methodProperties, parameters, values, l_accuracies); } } } if (isGeographic3DToGravityRelatedHeight(method(), false)) { const auto &fileParameter = parameterValue(EPSG_NAME_PARAMETER_GEOID_CORRECTION_FILENAME, EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { auto filename = fileParameter->valueFile(); if (databaseContext->lookForGridAlternative( filename, projFilename, projGridFormat, inverseDirection)) { if (inverseDirection) { throw util::UnsupportedOperationException( "Inverse direction for " "Geographic3DToGravityRelatedHeight not supported"); } if (filename == projFilename) { return self; } auto parameters = std::vector{ createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME)}; #ifdef disabled_for_now if (inverseDirection) { return create(createPropertiesForInverse( self.as_nullable().get(), true, false), targetCRS(), sourceCRS(), nullptr, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename( projFilename)}, coordinateOperationAccuracies()) ->inverseAsTransformation(); } else #endif { return create( createSimilarPropertiesTransformation(self), sourceCRS(), targetCRS(), nullptr, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename(projFilename)}, coordinateOperationAccuracies()); } } } } if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON || methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD || methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_GTX) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { auto filename = fileParameter->valueFile(); if (databaseContext->lookForGridAlternative( filename, projFilename, projGridFormat, inverseDirection)) { if (filename == projFilename) { if (inverseDirection) { throw util::UnsupportedOperationException( "Inverse direction for " + projFilename + " not supported"); } return self; } auto parameters = std::vector{ createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE)}; if (inverseDirection) { return create(createPropertiesForInverse( self.as_nullable().get(), true, false), targetCRS(), sourceCRS(), nullptr, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename( projFilename)}, coordinateOperationAccuracies()) ->inverseAsTransformation(); } else { return create( createSimilarPropertiesTransformation(self), sourceCRS(), targetCRS(), nullptr, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename(projFilename)}, coordinateOperationAccuracies()); } } } } return self; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static void ThrowExceptionNotGeodeticGeographic(const char *trfrm_name) { throw io::FormattingException(concat("Can apply ", std::string(trfrm_name), " only to GeodeticCRS / " "GeographicCRS")); } // --------------------------------------------------------------------------- static void setupPROJGeodeticSourceCRS(io::PROJStringFormatter *formatter, const crs::CRSNNPtr &crs, bool addPushV3, const char *trfrm_name) { auto sourceCRSGeog = dynamic_cast(crs.get()); if (sourceCRSGeog) { formatter->startInversion(); sourceCRSGeog->_exportToPROJString(formatter); formatter->stopInversion(); if (addPushV3) { formatter->addStep("push"); formatter->addParam("v_3"); } formatter->addStep("cart"); sourceCRSGeog->ellipsoid()->_exportToPROJString(formatter); } else { auto sourceCRSGeod = dynamic_cast(crs.get()); if (!sourceCRSGeod) { ThrowExceptionNotGeodeticGeographic(trfrm_name); } formatter->startInversion(); sourceCRSGeod->addGeocentricUnitConversionIntoPROJString(formatter); formatter->stopInversion(); } } // --------------------------------------------------------------------------- static void setupPROJGeodeticTargetCRS(io::PROJStringFormatter *formatter, const crs::CRSNNPtr &crs, bool addPopV3, const char *trfrm_name) { auto targetCRSGeog = dynamic_cast(crs.get()); if (targetCRSGeog) { formatter->addStep("cart"); formatter->setCurrentStepInverted(true); targetCRSGeog->ellipsoid()->_exportToPROJString(formatter); if (addPopV3) { formatter->addStep("pop"); formatter->addParam("v_3"); } targetCRSGeog->_exportToPROJString(formatter); } else { auto targetCRSGeod = dynamic_cast(crs.get()); if (!targetCRSGeod) { ThrowExceptionNotGeodeticGeographic(trfrm_name); } targetCRSGeod->addGeocentricUnitConversionIntoPROJString(formatter); } } //! @endcond // --------------------------------------------------------------------------- void Transformation::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { if (formatter->convention() == io::PROJStringFormatter::Convention::PROJ_4) { throw io::FormattingException( "Transformation cannot be exported as a PROJ.4 string"); } formatter->setCoordinateOperationOptimizations(true); bool positionVectorConvention = true; bool sevenParamsTransform = false; bool threeParamsTransform = false; bool fifteenParamsTransform = false; const auto &l_method = method(); const int methodEPSGCode = l_method->getEPSGCode(); const auto &methodName = l_method->nameStr(); const auto paramCount = parameterValues().size(); const bool l_isTimeDependent = isTimeDependent(methodName); const bool isPositionVector = ci_find(methodName, "Position Vector") != std::string::npos || ci_find(methodName, "PV") != std::string::npos; const bool isCoordinateFrame = ci_find(methodName, "Coordinate Frame") != std::string::npos || ci_find(methodName, "CF") != std::string::npos; if ((paramCount == 7 && isCoordinateFrame && !l_isTimeDependent) || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_3D) { positionVectorConvention = false; sevenParamsTransform = true; } else if ( (paramCount == 15 && isCoordinateFrame && l_isTimeDependent) || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_3D) { positionVectorConvention = false; fifteenParamsTransform = true; } else if ((paramCount == 7 && isPositionVector && !l_isTimeDependent) || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D) { sevenParamsTransform = true; } else if ( (paramCount == 15 && isPositionVector && l_isTimeDependent) || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_3D) { fifteenParamsTransform = true; } else if ((paramCount == 3 && ci_find(methodName, "Geocentric translations") != std::string::npos) || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_3D) { threeParamsTransform = true; } if (threeParamsTransform || sevenParamsTransform || fifteenParamsTransform) { double x = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION); double y = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION); double z = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION); bool addPushPopV3 = (methodEPSGCode == EPSG_CODE_METHOD_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_COORDINATE_FRAME_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_TIME_DEPENDENT_POSITION_VECTOR_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_GEOCENTRIC_TRANSLATION_GEOGRAPHIC_2D); setupPROJGeodeticSourceCRS(formatter, sourceCRS(), addPushPopV3, "Helmert"); formatter->addStep("helmert"); formatter->addParam("x", x); formatter->addParam("y", y); formatter->addParam("z", z); if (sevenParamsTransform || fifteenParamsTransform) { double rx = parameterValueNumeric(EPSG_CODE_PARAMETER_X_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double ry = parameterValueNumeric(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double rz = parameterValueNumeric(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double scaleDiff = parameterValueNumeric(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE, common::UnitOfMeasure::PARTS_PER_MILLION); formatter->addParam("rx", rx); formatter->addParam("ry", ry); formatter->addParam("rz", rz); formatter->addParam("s", scaleDiff); if (fifteenParamsTransform) { double rate_x = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_X_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR); double rate_y = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Y_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR); double rate_z = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Z_AXIS_TRANSLATION, common::UnitOfMeasure::METRE_PER_YEAR); double rate_rx = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_X_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR); double rate_ry = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Y_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR); double rate_rz = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_Z_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND_PER_YEAR); double rate_scaleDiff = parameterValueNumeric( EPSG_CODE_PARAMETER_RATE_SCALE_DIFFERENCE, common::UnitOfMeasure::PPM_PER_YEAR); double referenceEpochYear = parameterValueNumeric(EPSG_CODE_PARAMETER_REFERENCE_EPOCH, common::UnitOfMeasure::YEAR); formatter->addParam("dx", rate_x); formatter->addParam("dy", rate_y); formatter->addParam("dz", rate_z); formatter->addParam("drx", rate_rx); formatter->addParam("dry", rate_ry); formatter->addParam("drz", rate_rz); formatter->addParam("ds", rate_scaleDiff); formatter->addParam("t_epoch", referenceEpochYear); } if (positionVectorConvention) { formatter->addParam("convention", "position_vector"); } else { formatter->addParam("convention", "coordinate_frame"); } } setupPROJGeodeticTargetCRS(formatter, targetCRS(), addPushPopV3, "Helmert"); return; } if (methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_3D || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_3D || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_2D) { positionVectorConvention = isPositionVector || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOCENTRIC || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_3D || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_2D; double x = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION); double y = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION); double z = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION); double rx = parameterValueNumeric(EPSG_CODE_PARAMETER_X_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double ry = parameterValueNumeric(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double rz = parameterValueNumeric(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION, common::UnitOfMeasure::ARC_SECOND); double scaleDiff = parameterValueNumeric(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE, common::UnitOfMeasure::PARTS_PER_MILLION); double px = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_ORDINATE_1_EVAL_POINT); double py = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_ORDINATE_2_EVAL_POINT); double pz = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_ORDINATE_3_EVAL_POINT); bool addPushPopV3 = (methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_PV_GEOGRAPHIC_2D || methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY_BADEKAS_CF_GEOGRAPHIC_2D); setupPROJGeodeticSourceCRS(formatter, sourceCRS(), addPushPopV3, "Molodensky-Badekas"); formatter->addStep("molobadekas"); formatter->addParam("x", x); formatter->addParam("y", y); formatter->addParam("z", z); formatter->addParam("rx", rx); formatter->addParam("ry", ry); formatter->addParam("rz", rz); formatter->addParam("s", scaleDiff); formatter->addParam("px", px); formatter->addParam("py", py); formatter->addParam("pz", pz); if (positionVectorConvention) { formatter->addParam("convention", "position_vector"); } else { formatter->addParam("convention", "coordinate_frame"); } setupPROJGeodeticTargetCRS(formatter, targetCRS(), addPushPopV3, "Molodensky-Badekas"); return; } if (methodEPSGCode == EPSG_CODE_METHOD_MOLODENSKY || methodEPSGCode == EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY) { double x = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION); double y = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION); double z = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION); double da = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_SEMI_MAJOR_AXIS_DIFFERENCE); double df = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_FLATTENING_DIFFERENCE); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { throw io::FormattingException( "Can apply Molodensky only to GeographicCRS"); } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { throw io::FormattingException( "Can apply Molodensky only to GeographicCRS"); } formatter->startInversion(); sourceCRSGeog->_exportToPROJString(formatter); formatter->stopInversion(); formatter->addStep("molodensky"); sourceCRSGeog->ellipsoid()->_exportToPROJString(formatter); formatter->addParam("dx", x); formatter->addParam("dy", y); formatter->addParam("dz", z); formatter->addParam("da", da); formatter->addParam("df", df); if (ci_find(methodName, "Abridged") != std::string::npos || methodEPSGCode == EPSG_CODE_METHOD_ABRIDGED_MOLODENSKY) { formatter->addParam("abridged"); } targetCRSGeog->_exportToPROJString(formatter); return; } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC2D_OFFSETS) { double offsetLat = parameterValueNumeric(EPSG_CODE_PARAMETER_LATITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); double offsetLong = parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { throw io::FormattingException( "Can apply Geographic 2D offsets only to GeographicCRS"); } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { throw io::FormattingException( "Can apply Geographic 2D offsets only to GeographicCRS"); } formatter->startInversion(); sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); if (offsetLat != 0.0 || offsetLong != 0.0) { formatter->addStep("geogoffset"); formatter->addParam("dlat", offsetLat); formatter->addParam("dlon", offsetLong); } targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); return; } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSETS) { double offsetLat = parameterValueNumeric(EPSG_CODE_PARAMETER_LATITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); double offsetLong = parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); double offsetHeight = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_VERTICAL_OFFSET); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { throw io::FormattingException( "Can apply Geographic 3D offsets only to GeographicCRS"); } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { throw io::FormattingException( "Can apply Geographic 3D offsets only to GeographicCRS"); } formatter->startInversion(); sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); if (offsetLat != 0.0 || offsetLong != 0.0 || offsetHeight != 0.0) { formatter->addStep("geogoffset"); formatter->addParam("dlat", offsetLat); formatter->addParam("dlon", offsetLong); formatter->addParam("dh", offsetHeight); } targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); return; } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC2D_WITH_HEIGHT_OFFSETS) { double offsetLat = parameterValueNumeric(EPSG_CODE_PARAMETER_LATITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); double offsetLong = parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET, common::UnitOfMeasure::ARC_SECOND); double offsetHeight = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_GEOID_UNDULATION); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { auto sourceCRSCompound = dynamic_cast(sourceCRS().get()); if (sourceCRSCompound) { sourceCRSGeog = sourceCRSCompound->extractGeographicCRS().get(); } if (!sourceCRSGeog) { throw io::FormattingException("Can apply Geographic 2D with " "height offsets only to " "GeographicCRS / CompoundCRS"); } } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { auto targetCRSCompound = dynamic_cast(targetCRS().get()); if (targetCRSCompound) { targetCRSGeog = targetCRSCompound->extractGeographicCRS().get(); } if (!targetCRSGeog) { throw io::FormattingException("Can apply Geographic 2D with " "height offsets only to " "GeographicCRS / CompoundCRS"); } } formatter->startInversion(); sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); if (offsetLat != 0.0 || offsetLong != 0.0 || offsetHeight != 0.0) { formatter->addStep("geogoffset"); formatter->addParam("dlat", offsetLat); formatter->addParam("dlon", offsetLong); formatter->addParam("dh", offsetHeight); } targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); return; } if (methodEPSGCode == EPSG_CODE_METHOD_VERTICAL_OFFSET) { auto sourceCRSVert = dynamic_cast(sourceCRS().get()); if (!sourceCRSVert) { throw io::FormattingException( "Can apply Vertical offset only to VerticalCRS"); } auto targetCRSVert = dynamic_cast(targetCRS().get()); if (!targetCRSVert) { throw io::FormattingException( "Can apply Vertical offset only to VerticalCRS"); } auto offsetHeight = parameterValueNumericAsSI(EPSG_CODE_PARAMETER_VERTICAL_OFFSET); formatter->startInversion(); sourceCRSVert->addLinearUnitConvert(formatter); formatter->stopInversion(); formatter->addStep("geogoffset"); formatter->addParam("dh", offsetHeight); targetCRSVert->addLinearUnitConvert(formatter); return; } // Substitute grid names with PROJ friendly names. if (formatter->databaseContext()) { auto alternate = substitutePROJAlternativeGridNames( NN_NO_CHECK(formatter->databaseContext())); auto self = NN_NO_CHECK(std::dynamic_pointer_cast( shared_from_this().as_nullable())); if (alternate != self) { alternate->_exportToPROJString(formatter); return; } } const bool isMethodInverseOf = starts_with(methodName, INVERSE_OF); const auto &NTv1Filename = _getNTv1Filename(this, true); const auto &NTv2Filename = _getNTv2Filename(this, true); const auto &CTABLE2Filename = _getCTABLE2Filename(this, true); const auto &hGridShiftFilename = !NTv1Filename.empty() ? NTv1Filename : !NTv2Filename.empty() ? NTv2Filename : CTABLE2Filename; if (!hGridShiftFilename.empty()) { auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { throw io::FormattingException( concat("Can apply ", methodName, " only to GeographicCRS")); } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { throw io::FormattingException( concat("Can apply ", methodName, " only to GeographicCRS")); } formatter->startInversion(); sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); if (isMethodInverseOf) { formatter->startInversion(); } formatter->addStep("hgridshift"); formatter->addParam("grids", hGridShiftFilename); if (isMethodInverseOf) { formatter->stopInversion(); } targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); return; } const auto &heightFilename = _getHeightToGeographic3DFilename(this, true); if (!heightFilename.empty()) { if (isMethodInverseOf) { formatter->startInversion(); } formatter->addStep("vgridshift"); formatter->addParam("grids", heightFilename); formatter->addParam("multiplier", 1.0); if (isMethodInverseOf) { formatter->stopInversion(); } return; } if (isGeographic3DToGravityRelatedHeight(method(), true)) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_GEOID_CORRECTION_FILENAME, EPSG_CODE_PARAMETER_GEOID_CORRECTION_FILENAME); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { auto filename = fileParameter->valueFile(); bool doInversion = isMethodInverseOf; // The EPSG Geog3DToHeight is the reverse convention of PROJ ! doInversion = !doInversion; if (doInversion) { formatter->startInversion(); } formatter->addStep("vgridshift"); formatter->addParam("grids", filename); formatter->addParam("multiplier", 1.0); if (doInversion) { formatter->stopInversion(); } return; } } if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { formatter->addStep("vgridshift"); // The vertcon grids go from NGVD 29 to NAVD 88, with units // in millimeter (see // https://github.com/OSGeo/proj.4/issues/1071) formatter->addParam("grids", fileParameter->valueFile()); formatter->addParam("multiplier", 0.001); return; } } if (methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD || methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_GTX) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); if (fileParameter && fileParameter->type() == ParameterValue::Type::FILENAME) { formatter->addStep("vgridshift"); formatter->addParam("grids", fileParameter->valueFile()); formatter->addParam("multiplier", 1.0); return; } } if (isLongitudeRotation()) { double offsetDeg = parameterValueNumeric(EPSG_CODE_PARAMETER_LONGITUDE_OFFSET, common::UnitOfMeasure::DEGREE); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); if (!sourceCRSGeog) { throw io::FormattingException( concat("Can apply ", methodName, " only to GeographicCRS")); } auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (!targetCRSGeog) { throw io::FormattingException( concat("Can apply ", methodName + " only to GeographicCRS")); } if (!sourceCRSGeog->ellipsoid()->_isEquivalentTo( targetCRSGeog->ellipsoid().get(), util::IComparable::Criterion::EQUIVALENT)) { // This is arguable if we should check this... throw io::FormattingException("Can apply Longitude rotation " "only to SRS with same " "ellipsoid"); } formatter->startInversion(); sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); bool done = false; if (offsetDeg != 0.0) { // Optimization: as we are doing nominally a +step=inv, // if the negation of the offset value is a well-known name, // then use forward case with this name. auto projPMName = datum::PrimeMeridian::getPROJStringWellKnownName( common::Angle(-offsetDeg)); if (!projPMName.empty()) { done = true; formatter->addStep("longlat"); sourceCRSGeog->ellipsoid()->_exportToPROJString(formatter); formatter->addParam("pm", projPMName); } } if (!done) { // To actually add the offset, we must use the reverse longlat // operation. formatter->startInversion(); formatter->addStep("longlat"); sourceCRSGeog->ellipsoid()->_exportToPROJString(formatter); datum::PrimeMeridian::create(util::PropertyMap(), common::Angle(offsetDeg)) ->_exportToPROJString(formatter); formatter->stopInversion(); } targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); return; } if (exportToPROJStringGeneric(formatter)) { return; } throw io::FormattingException("Unimplemented"); } // --------------------------------------------------------------------------- bool SingleOperation::exportToPROJStringGeneric( io::PROJStringFormatter *formatter) const { const int methodEPSGCode = method()->getEPSGCode(); if (methodEPSGCode == EPSG_CODE_METHOD_AFFINE_PARAMETRIC_TRANSFORMATION) { const double A0 = parameterValueMeasure(EPSG_CODE_PARAMETER_A0).value(); const double A1 = parameterValueMeasure(EPSG_CODE_PARAMETER_A1).value(); const double A2 = parameterValueMeasure(EPSG_CODE_PARAMETER_A2).value(); const double B0 = parameterValueMeasure(EPSG_CODE_PARAMETER_B0).value(); const double B1 = parameterValueMeasure(EPSG_CODE_PARAMETER_B1).value(); const double B2 = parameterValueMeasure(EPSG_CODE_PARAMETER_B2).value(); // Do not mess with axis unit and order for that transformation formatter->addStep("affine"); formatter->addParam("xoff", A0); formatter->addParam("s11", A1); formatter->addParam("s12", A2); formatter->addParam("yoff", B0); formatter->addParam("s21", B1); formatter->addParam("s22", B2); return true; } if (isAxisOrderReversal(methodEPSGCode)) { formatter->addStep("axisswap"); formatter->addParam("order", "2,1"); auto sourceCRSGeog = dynamic_cast(sourceCRS().get()); auto targetCRSGeog = dynamic_cast(targetCRS().get()); if (sourceCRSGeog && targetCRSGeog) { const auto &unitSrc = sourceCRSGeog->coordinateSystem()->axisList()[0]->unit(); const auto &unitDst = targetCRSGeog->coordinateSystem()->axisList()[0]->unit(); if (!unitSrc._isEquivalentTo( unitDst, util::IComparable::Criterion::EQUIVALENT)) { formatter->addStep("unitconvert"); auto projUnit = unitSrc.exportToPROJString(); if (projUnit.empty()) { formatter->addParam("xy_in", unitSrc.conversionToSI()); } else { formatter->addParam("xy_in", projUnit); } projUnit = unitDst.exportToPROJString(); if (projUnit.empty()) { formatter->addParam("xy_out", unitDst.conversionToSI()); } else { formatter->addParam("xy_out", projUnit); } } } return true; } if (methodEPSGCode == EPSG_CODE_METHOD_GEOGRAPHIC_GEOCENTRIC) { auto sourceCRSGeod = dynamic_cast(sourceCRS().get()); auto targetCRSGeod = dynamic_cast(targetCRS().get()); if (sourceCRSGeod && targetCRSGeod) { auto sourceCRSGeog = dynamic_cast(sourceCRSGeod); auto targetCRSGeog = dynamic_cast(targetCRSGeod); bool isSrcGeocentric = sourceCRSGeod->isGeocentric(); bool isSrcGeographic = sourceCRSGeog != nullptr; bool isTargetGeocentric = targetCRSGeod->isGeocentric(); bool isTargetGeographic = targetCRSGeog != nullptr; if ((isSrcGeocentric && isTargetGeographic) || (isSrcGeographic && isTargetGeocentric)) { formatter->startInversion(); sourceCRSGeod->_exportToPROJString(formatter); formatter->stopInversion(); targetCRSGeod->_exportToPROJString(formatter); return true; } } throw io::FormattingException("Invalid nature of source and/or " "targetCRS for Geographic/Geocentric " "conversion"); } if (methodEPSGCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT) { double convFactor = parameterValueNumericAsSI( EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR); auto uom = common::UnitOfMeasure(std::string(), convFactor, common::UnitOfMeasure::Type::LINEAR) .exportToPROJString(); auto reverse_uom = common::UnitOfMeasure(std::string(), 1.0 / convFactor, common::UnitOfMeasure::Type::LINEAR) .exportToPROJString(); if (uom == "m") { // do nothing } else if (!uom.empty()) { formatter->addStep("unitconvert"); formatter->addParam("z_in", uom); formatter->addParam("z_out", "m"); } else if (!reverse_uom.empty()) { formatter->addStep("unitconvert"); formatter->addParam("z_in", "m"); formatter->addParam("z_out", reverse_uom); } else { formatter->addStep("affine"); formatter->addParam("s33", convFactor); } return true; } if (methodEPSGCode == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) { formatter->addStep("axisswap"); formatter->addParam("order", "1,2,-3"); return true; } return false; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress PointMotionOperation::~PointMotionOperation() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ConcatenatedOperation::Private { std::vector operations_{}; bool computedName_ = false; explicit Private(const std::vector &operationsIn) : operations_(operationsIn) {} Private(const Private &) = default; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ConcatenatedOperation::~ConcatenatedOperation() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ConcatenatedOperation::ConcatenatedOperation(const ConcatenatedOperation &other) : CoordinateOperation(other), d(internal::make_unique(*(other.d))) {} //! @endcond // --------------------------------------------------------------------------- ConcatenatedOperation::ConcatenatedOperation( const std::vector &operationsIn) : CoordinateOperation(), d(internal::make_unique(operationsIn)) {} // --------------------------------------------------------------------------- /** \brief Return the operation steps of the concatenated operation. * * @return the operation steps. */ const std::vector & ConcatenatedOperation::operations() const { return d->operations_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool compareStepCRS(const crs::CRS *a, const crs::CRS *b) { const auto &aIds = a->identifiers(); const auto &bIds = b->identifiers(); if (aIds.size() == 1 && bIds.size() == 1 && aIds[0]->code() == bIds[0]->code() && *aIds[0]->codeSpace() == *bIds[0]->codeSpace()) { return true; } return a->_isEquivalentTo(b, util::IComparable::Criterion::EQUIVALENT); } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a ConcatenatedOperation * * @param properties See \ref general_properties. At minimum the name should * be * defined. * @param operationsIn Vector of the CoordinateOperation steps. * @param accuracies Vector of positional accuracy (might be empty). * @return new Transformation. * @throws InvalidOperation */ ConcatenatedOperationNNPtr ConcatenatedOperation::create( const util::PropertyMap &properties, const std::vector &operationsIn, const std::vector &accuracies) // throw InvalidOperation { if (operationsIn.size() < 2) { throw InvalidOperation( "ConcatenatedOperation must have at least 2 operations"); } crs::CRSPtr lastTargetCRS; for (size_t i = 0; i < operationsIn.size(); i++) { auto l_sourceCRS = operationsIn[i]->sourceCRS(); auto l_targetCRS = operationsIn[i]->targetCRS(); if (l_sourceCRS == nullptr || l_targetCRS == nullptr) { throw InvalidOperation("At least one of the operation lacks a " "source and/or target CRS"); } if (i >= 1) { if (!compareStepCRS(l_sourceCRS.get(), lastTargetCRS.get())) { #ifdef DEBUG_CONCATENATED_OPERATION std::cerr << "Step " << i - 1 << ": " << operationsIn[i - 1]->nameStr() << std::endl; std::cerr << "Step " << i << ": " << operationsIn[i]->nameStr() << std::endl; { auto f(io::WKTFormatter::create( io::WKTFormatter::Convention::WKT2_2019)); std::cerr << "Source CRS of step " << i << ":" << std::endl; std::cerr << l_sourceCRS->exportToWKT(f.get()) << std::endl; } { auto f(io::WKTFormatter::create( io::WKTFormatter::Convention::WKT2_2019)); std::cerr << "Target CRS of step " << i - 1 << ":" << std::endl; std::cerr << lastTargetCRS->exportToWKT(f.get()) << std::endl; } #endif throw InvalidOperation( "Inconsistent chaining of CRS in operations"); } } lastTargetCRS = l_targetCRS; } auto op = ConcatenatedOperation::nn_make_shared( operationsIn); op->assignSelf(op); op->setProperties(properties); op->setCRSs(NN_NO_CHECK(operationsIn[0]->sourceCRS()), NN_NO_CHECK(operationsIn.back()->targetCRS()), nullptr); op->setAccuracies(accuracies); #ifdef DEBUG_CONCATENATED_OPERATION { auto f( io::WKTFormatter::create(io::WKTFormatter::Convention::WKT2_2019)); std::cerr << "ConcatenatedOperation::create()" << std::endl; std::cerr << op->exportToWKT(f.get()) << std::endl; } #endif return op; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- void ConcatenatedOperation::fixStepsDirection( const crs::CRSNNPtr &concatOpSourceCRS, const crs::CRSNNPtr &concatOpTargetCRS, std::vector &operationsInOut) { // Set of heuristics to assign CRS to steps, and possibly reverse them. for (size_t i = 0; i < operationsInOut.size(); ++i) { auto &op = operationsInOut[i]; auto l_sourceCRS = op->sourceCRS(); auto l_targetCRS = op->targetCRS(); auto conv = dynamic_cast(op.get()); if (conv && i == 0 && !l_sourceCRS && !l_targetCRS) { auto derivedCRS = dynamic_cast(concatOpSourceCRS.get()); if (derivedCRS) { if (i + 1 < operationsInOut.size()) { // use the sourceCRS of the next operation as our target CRS l_targetCRS = operationsInOut[i + 1]->sourceCRS(); // except if it looks like the next operation should // actually be reversed !!! if (l_targetCRS && !compareStepCRS(l_targetCRS.get(), derivedCRS->baseCRS().get()) && operationsInOut[i + 1]->targetCRS() && compareStepCRS( operationsInOut[i + 1]->targetCRS().get(), derivedCRS->baseCRS().get())) { l_targetCRS = operationsInOut[i + 1]->targetCRS(); } } if (!l_targetCRS) { l_targetCRS = derivedCRS->baseCRS().as_nullable(); } auto invConv = util::nn_dynamic_pointer_cast(op); auto nn_targetCRS = NN_NO_CHECK(l_targetCRS); if (invConv) { invConv->inverse()->setCRSs(nn_targetCRS, concatOpSourceCRS, nullptr); op->setCRSs(concatOpSourceCRS, nn_targetCRS, nullptr); } else { op->setCRSs(nn_targetCRS, concatOpSourceCRS, nullptr); op = op->inverse(); } } else if (i + 1 < operationsInOut.size()) { /* coverity[copy_paste_error] */ l_targetCRS = operationsInOut[i + 1]->sourceCRS(); if (l_targetCRS) { op->setCRSs(concatOpSourceCRS, NN_NO_CHECK(l_targetCRS), nullptr); } } } else if (conv && i + 1 == operationsInOut.size() && !l_sourceCRS && !l_targetCRS) { auto derivedCRS = dynamic_cast(concatOpTargetCRS.get()); if (derivedCRS) { if (i >= 1) { // use the sourceCRS of the previous operation as our source // CRS l_sourceCRS = operationsInOut[i - 1]->targetCRS(); // except if it looks like the previous operation should // actually be reversed !!! if (l_sourceCRS && !compareStepCRS(l_sourceCRS.get(), derivedCRS->baseCRS().get()) && operationsInOut[i - 1]->sourceCRS() && compareStepCRS( operationsInOut[i - 1]->sourceCRS().get(), derivedCRS->baseCRS().get())) { l_targetCRS = operationsInOut[i - 1]->sourceCRS(); } } if (!l_sourceCRS) { l_sourceCRS = derivedCRS->baseCRS().as_nullable(); } op->setCRSs(NN_NO_CHECK(l_sourceCRS), concatOpTargetCRS, nullptr); } else if (i >= 1) { l_sourceCRS = operationsInOut[i - 1]->targetCRS(); if (l_sourceCRS) { op->setCRSs(NN_NO_CHECK(l_sourceCRS), concatOpTargetCRS, nullptr); } } } else if (conv && i > 0 && i < operationsInOut.size() - 1) { // For an intermediate conversion, use the target CRS of the // previous step and the source CRS of the next step l_sourceCRS = operationsInOut[i - 1]->targetCRS(); l_targetCRS = operationsInOut[i + 1]->sourceCRS(); if (l_sourceCRS && l_targetCRS) { op->setCRSs(NN_NO_CHECK(l_sourceCRS), NN_NO_CHECK(l_targetCRS), nullptr); } } else if (!conv && l_sourceCRS && l_targetCRS) { const auto isGeographic = [](const crs::CRS *crs) -> bool { return dynamic_cast(crs) != nullptr; }; const auto isGeocentric = [](const crs::CRS *crs) -> bool { auto geodCRS = dynamic_cast(crs); if (geodCRS && geodCRS->coordinateSystem()->axisList().size() == 3) return true; return false; }; // Transformations might be mentioned in their forward directions, // whereas we should instead use the reverse path. auto prevOpTarget = (i == 0) ? concatOpSourceCRS.as_nullable() : operationsInOut[i - 1]->targetCRS(); if (compareStepCRS(l_sourceCRS.get(), prevOpTarget.get())) { // do nothing } else if (compareStepCRS(l_targetCRS.get(), prevOpTarget.get())) { op = op->inverse(); } // Below is needed for EPSG:9103 which chains NAD83(2011) geographic // 2D with NAD83(2011) geocentric else if (l_sourceCRS->nameStr() == prevOpTarget->nameStr() && ((isGeographic(l_sourceCRS.get()) && isGeocentric(prevOpTarget.get())) || (isGeocentric(l_sourceCRS.get()) && isGeographic(prevOpTarget.get())))) { auto newOp(Conversion::createGeographicGeocentric( NN_NO_CHECK(prevOpTarget), NN_NO_CHECK(l_sourceCRS))); operationsInOut.insert(operationsInOut.begin() + i, newOp); } else if (l_targetCRS->nameStr() == prevOpTarget->nameStr() && ((isGeographic(l_targetCRS.get()) && isGeocentric(prevOpTarget.get())) || (isGeocentric(l_targetCRS.get()) && isGeographic(prevOpTarget.get())))) { auto newOp(Conversion::createGeographicGeocentric( NN_NO_CHECK(prevOpTarget), NN_NO_CHECK(l_targetCRS))); operationsInOut.insert(operationsInOut.begin() + i, newOp); } } } if (!operationsInOut.empty()) { auto l_sourceCRS = operationsInOut.front()->sourceCRS(); if (l_sourceCRS && !compareStepCRS(l_sourceCRS.get(), concatOpSourceCRS.get())) { throw InvalidOperation("The source CRS of the first step of " "concatenated operation is not the same " "as the source CRS of the concatenated " "operation itself"); } auto l_targetCRS = operationsInOut.back()->targetCRS(); if (l_targetCRS && !compareStepCRS(l_targetCRS.get(), concatOpTargetCRS.get())) { throw InvalidOperation("The target CRS of the last step of " "concatenated operation is not the same " "as the target CRS of the concatenated " "operation itself"); } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static std::string computeConcatenatedName( const std::vector &flattenOps) { std::string name; for (const auto &subOp : flattenOps) { if (!name.empty()) { name += " + "; } const auto &l_name = subOp->nameStr(); if (l_name.empty()) { name += "unnamed"; } else { name += l_name; } } return name; } //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a ConcatenatedOperation, or return a single * coordinate * operation. * * This computes its accuracy from the sum of its member operations, its * extent * * @param operationsIn Vector of the CoordinateOperation steps. * @param checkExtent Whether we should check the non-emptyness of the * intersection * of the extents of the operations * @throws InvalidOperation */ CoordinateOperationNNPtr ConcatenatedOperation::createComputeMetadata( const std::vector &operationsIn, bool checkExtent) // throw InvalidOperation { util::PropertyMap properties; if (operationsIn.size() == 1) { return operationsIn[0]; } std::vector flattenOps; bool hasBallparkTransformation = false; for (const auto &subOp : operationsIn) { hasBallparkTransformation |= subOp->hasBallparkTransformation(); auto subOpConcat = dynamic_cast(subOp.get()); if (subOpConcat) { auto subOps = subOpConcat->operations(); for (const auto &subSubOp : subOps) { flattenOps.emplace_back(subSubOp); } } else { flattenOps.emplace_back(subOp); } } if (flattenOps.size() == 1) { return flattenOps[0]; } properties.set(common::IdentifiedObject::NAME_KEY, computeConcatenatedName(flattenOps)); bool emptyIntersection = false; auto extent = getExtent(flattenOps, false, emptyIntersection); if (checkExtent && emptyIntersection) { std::string msg( "empty intersection of area of validity of concatenated " "operations"); throw InvalidOperationEmptyIntersection(msg); } if (extent) { properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(extent)); } std::vector accuracies; const double accuracy = getAccuracy(flattenOps); if (accuracy >= 0.0) { accuracies.emplace_back( metadata::PositionalAccuracy::create(toString(accuracy))); } auto op = create(properties, flattenOps, accuracies); op->setHasBallparkTransformation(hasBallparkTransformation); op->d->computedName_ = true; return op; } // --------------------------------------------------------------------------- CoordinateOperationNNPtr ConcatenatedOperation::inverse() const { std::vector inversedOperations; auto l_operations = operations(); inversedOperations.reserve(l_operations.size()); for (const auto &operation : l_operations) { inversedOperations.emplace_back(operation->inverse()); } std::reverse(inversedOperations.begin(), inversedOperations.end()); auto properties = createPropertiesForInverse(this, false, false); if (d->computedName_) { properties.set(common::IdentifiedObject::NAME_KEY, computeConcatenatedName(inversedOperations)); } auto op = create(properties, inversedOperations, coordinateOperationAccuracies()); op->d->computedName_ = d->computedName_; op->setHasBallparkTransformation(hasBallparkTransformation()); return op; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ConcatenatedOperation::_exportToWKT(io::WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2 || !formatter->use2019Keywords()) { throw io::FormattingException( "Transformation can only be exported to WKT2:2019"); } formatter->startNode(io::WKTConstants::CONCATENATEDOPERATION, !identifiers().empty()); formatter->addQuotedString(nameStr()); if (isWKT2 && formatter->use2019Keywords()) { const auto &version = operationVersion(); if (version.has_value()) { formatter->startNode(io::WKTConstants::VERSION, false); formatter->addQuotedString(*version); formatter->endNode(); } } exportSourceCRSAndTargetCRSToWKT(this, formatter); const bool canExportOperationId = !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId()); const bool hasDomains = !domains().empty(); if (hasDomains) { formatter->pushDisableUsage(); } for (const auto &operation : operations()) { formatter->startNode(io::WKTConstants::STEP, false); if (canExportOperationId && !operation->identifiers().empty()) { // fake that top node has no id, so that the operation id is // considered formatter->pushHasId(false); operation->_exportToWKT(formatter); formatter->popHasId(); } else { operation->_exportToWKT(formatter); } formatter->endNode(); } if (hasDomains) { formatter->popDisableUsage(); } ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ConcatenatedOperation::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext("ConcatenatedOperation", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } writer.AddObjKey("source_crs"); formatter->setAllowIDInImmediateChild(); sourceCRS()->_exportToJSON(formatter); writer.AddObjKey("target_crs"); formatter->setAllowIDInImmediateChild(); targetCRS()->_exportToJSON(formatter); writer.AddObjKey("steps"); { auto parametersContext(writer.MakeArrayContext(false)); for (const auto &operation : operations()) { formatter->setAllowIDInImmediateChild(); operation->_exportToJSON(formatter); } } ObjectUsage::baseExportToJSON(formatter); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateOperationNNPtr ConcatenatedOperation::_shallowClone() const { auto op = ConcatenatedOperation::nn_make_shared(*this); std::vector ops; for (const auto &subOp : d->operations_) { ops.emplace_back(subOp->shallowClone()); } op->d->operations_ = ops; op->assignSelf(op); op->setCRSs(this, false); return util::nn_static_pointer_cast(op); } //! @endcond // --------------------------------------------------------------------------- void ConcatenatedOperation::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { for (const auto &operation : operations()) { operation->_exportToPROJString(formatter); } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool ConcatenatedOperation::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherCO = dynamic_cast(other); if (otherCO == nullptr || (criterion == util::IComparable::Criterion::STRICT && !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) { return false; } const auto &steps = operations(); const auto &otherSteps = otherCO->operations(); if (steps.size() != otherSteps.size()) { return false; } for (size_t i = 0; i < steps.size(); i++) { if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion, dbContext)) { return false; } } return true; } //! @endcond // --------------------------------------------------------------------------- std::set ConcatenatedOperation::gridsNeeded( const io::DatabaseContextPtr &databaseContext) const { std::set res; for (const auto &operation : operations()) { const auto l_gridsNeeded = operation->gridsNeeded(databaseContext); for (const auto &gridDesc : l_gridsNeeded) { res.insert(gridDesc); } } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CoordinateOperationContext::Private { io::AuthorityFactoryPtr authorityFactory_{}; metadata::ExtentPtr extent_{}; double accuracy_ = 0.0; SourceTargetCRSExtentUse sourceAndTargetCRSExtentUse_ = CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST; SpatialCriterion spatialCriterion_ = CoordinateOperationContext::SpatialCriterion::STRICT_CONTAINMENT; bool usePROJNames_ = true; GridAvailabilityUse gridAvailabilityUse_ = GridAvailabilityUse::USE_FOR_SORTING; IntermediateCRSUse allowUseIntermediateCRS_ = CoordinateOperationContext:: IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION; std::vector> intermediateCRSAuthCodes_{}; bool discardSuperseded_ = true; }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateOperationContext::~CoordinateOperationContext() = default; //! @endcond // --------------------------------------------------------------------------- CoordinateOperationContext::CoordinateOperationContext() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- /** \brief Return the authority factory, or null */ const io::AuthorityFactoryPtr & CoordinateOperationContext::getAuthorityFactory() const { return d->authorityFactory_; } // --------------------------------------------------------------------------- /** \brief Return the desired area of interest, or null */ const metadata::ExtentPtr & CoordinateOperationContext::getAreaOfInterest() const { return d->extent_; } // --------------------------------------------------------------------------- /** \brief Set the desired area of interest, or null */ void CoordinateOperationContext::setAreaOfInterest( const metadata::ExtentPtr &extent) { d->extent_ = extent; } // --------------------------------------------------------------------------- /** \brief Return the desired accuracy (in metre), or 0 */ double CoordinateOperationContext::getDesiredAccuracy() const { return d->accuracy_; } // --------------------------------------------------------------------------- /** \brief Set the desired accuracy (in metre), or 0 */ void CoordinateOperationContext::setDesiredAccuracy(double accuracy) { d->accuracy_ = accuracy; } // --------------------------------------------------------------------------- /** \brief Set how source and target CRS extent should be used * when considering if a transformation can be used (only takes effect if * no area of interest is explicitly defined). * * The default is * CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST. */ void CoordinateOperationContext::setSourceAndTargetCRSExtentUse( SourceTargetCRSExtentUse use) { d->sourceAndTargetCRSExtentUse_ = use; } // --------------------------------------------------------------------------- /** \brief Return how source and target CRS extent should be used * when considering if a transformation can be used (only takes effect if * no area of interest is explicitly defined). * * The default is * CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST. */ CoordinateOperationContext::SourceTargetCRSExtentUse CoordinateOperationContext::getSourceAndTargetCRSExtentUse() const { return d->sourceAndTargetCRSExtentUse_; } // --------------------------------------------------------------------------- /** \brief Set the spatial criterion to use when comparing the area of * validity * of coordinate operations with the area of interest / area of validity of * source and target CRS. * * The default is STRICT_CONTAINMENT. */ void CoordinateOperationContext::setSpatialCriterion( SpatialCriterion criterion) { d->spatialCriterion_ = criterion; } // --------------------------------------------------------------------------- /** \brief Return the spatial criterion to use when comparing the area of * validity * of coordinate operations with the area of interest / area of validity of * source and target CRS. * * The default is STRICT_CONTAINMENT. */ CoordinateOperationContext::SpatialCriterion CoordinateOperationContext::getSpatialCriterion() const { return d->spatialCriterion_; } // --------------------------------------------------------------------------- /** \brief Set whether PROJ alternative grid names should be substituted to * the official authority names. * * This only has effect is an authority factory with a non-null database context * has been attached to this context. * * If set to false, it is still possible to * obtain later the substitution by using io::PROJStringFormatter::create() * with a non-null database context. * * The default is true. */ void CoordinateOperationContext::setUsePROJAlternativeGridNames( bool usePROJNames) { d->usePROJNames_ = usePROJNames; } // --------------------------------------------------------------------------- /** \brief Return whether PROJ alternative grid names should be substituted to * the official authority names. * * The default is true. */ bool CoordinateOperationContext::getUsePROJAlternativeGridNames() const { return d->usePROJNames_; } // --------------------------------------------------------------------------- /** \brief Return whether transformations that are superseded (but not * deprecated) * should be discarded. * * The default is true. */ bool CoordinateOperationContext::getDiscardSuperseded() const { return d->discardSuperseded_; } // --------------------------------------------------------------------------- /** \brief Set whether transformations that are superseded (but not deprecated) * should be discarded. * * The default is true. */ void CoordinateOperationContext::setDiscardSuperseded(bool discard) { d->discardSuperseded_ = discard; } // --------------------------------------------------------------------------- /** \brief Set how grid availability is used. * * The default is USE_FOR_SORTING. */ void CoordinateOperationContext::setGridAvailabilityUse( GridAvailabilityUse use) { d->gridAvailabilityUse_ = use; } // --------------------------------------------------------------------------- /** \brief Return how grid availability is used. * * The default is USE_FOR_SORTING. */ CoordinateOperationContext::GridAvailabilityUse CoordinateOperationContext::getGridAvailabilityUse() const { return d->gridAvailabilityUse_; } // --------------------------------------------------------------------------- /** \brief Set whether an intermediate pivot CRS can be used for researching * coordinate operations between a source and target CRS. * * Concretely if in the database there is an operation from A to C * (or C to A), and another one from C to B (or B to C), but no direct * operation between A and B, setting this parameter to * ALWAYS/IF_NO_DIRECT_TRANSFORMATION, allow chaining both operations. * * The current implementation is limited to researching one intermediate * step. * * By default, with the IF_NO_DIRECT_TRANSFORMATION stratgey, all potential * C candidates will be used if there is no direct tranformation. */ void CoordinateOperationContext::setAllowUseIntermediateCRS( IntermediateCRSUse use) { d->allowUseIntermediateCRS_ = use; } // --------------------------------------------------------------------------- /** \brief Return whether an intermediate pivot CRS can be used for researching * coordinate operations between a source and target CRS. * * Concretely if in the database there is an operation from A to C * (or C to A), and another one from C to B (or B to C), but no direct * operation between A and B, setting this parameter to * ALWAYS/IF_NO_DIRECT_TRANSFORMATION, allow chaining both operations. * * The default is IF_NO_DIRECT_TRANSFORMATION. */ CoordinateOperationContext::IntermediateCRSUse CoordinateOperationContext::getAllowUseIntermediateCRS() const { return d->allowUseIntermediateCRS_; } // --------------------------------------------------------------------------- /** \brief Restrict the potential pivot CRSs that can be used when trying to * build a coordinate operation between two CRS that have no direct operation. * * @param intermediateCRSAuthCodes a vector of (auth_name, code) that can be * used as potential pivot RS */ void CoordinateOperationContext::setIntermediateCRS( const std::vector> &intermediateCRSAuthCodes) { d->intermediateCRSAuthCodes_ = intermediateCRSAuthCodes; } // --------------------------------------------------------------------------- /** \brief Return the potential pivot CRSs that can be used when trying to * build a coordinate operation between two CRS that have no direct operation. * */ const std::vector> & CoordinateOperationContext::getIntermediateCRS() const { return d->intermediateCRSAuthCodes_; } // --------------------------------------------------------------------------- /** \brief Creates a context for a coordinate operation. * * If a non null authorityFactory is provided, the resulting context should * not be used simultaneously by more than one thread. * * If authorityFactory->getAuthority() is the empty string, then coordinate * operations from any authority will be searched, with the restrictions set * in the authority_to_authority_preference database table. * If authorityFactory->getAuthority() is set to "any", then coordinate * operations from any authority will be searched * If authorityFactory->getAuthority() is a non-empty string different of "any", * then coordinate operatiosn will be searched only in that authority namespace. * * @param authorityFactory Authority factory, or null if no database lookup * is allowed. * Use io::authorityFactory::create(context, std::string()) to allow all * authorities to be used. * @param extent Area of interest, or null if none is known. * @param accuracy Maximum allowed accuracy in metre, as specified in or * 0 to get best accuracy. * @return a new context. */ CoordinateOperationContextNNPtr CoordinateOperationContext::create( const io::AuthorityFactoryPtr &authorityFactory, const metadata::ExtentPtr &extent, double accuracy) { auto ctxt = NN_NO_CHECK( CoordinateOperationContext::make_unique()); ctxt->d->authorityFactory_ = authorityFactory; ctxt->d->extent_ = extent; ctxt->d->accuracy_ = accuracy; return ctxt; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct CoordinateOperationFactory::Private { struct Context { // This is the extent of the source CRS and target CRS of the initial // CoordinateOperationFactory::createOperations() public call, not // necessarily the ones of intermediate // CoordinateOperationFactory::Private::createOperations() calls. // This is used to compare transformations area of use against the // area of use of the source & target CRS. const metadata::ExtentPtr &extent1; const metadata::ExtentPtr &extent2; const CoordinateOperationContextNNPtr &context; bool inCreateOperationsWithDatumPivotAntiRecursion = false; bool inCreateOperationsGeogToVertWithAlternativeGeog = false; bool inCreateOperationsGeogToVertWithIntermediateVert = false; bool skipHorizontalTransformation = false; std::map, std::list>> cacheNameToCRS{}; Context(const metadata::ExtentPtr &extent1In, const metadata::ExtentPtr &extent2In, const CoordinateOperationContextNNPtr &contextIn) : extent1(extent1In), extent2(extent2In), context(contextIn) {} }; static std::vector createOperations(const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Context &context); private: static constexpr bool allowEmptyIntersection = true; static void buildCRSIds(const crs::CRSNNPtr &crs, Private::Context &context, std::list> &ids); static std::vector findOpsInRegistryDirect( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, bool &resNonEmptyBeforeFiltering); static std::vector findOpsInRegistryDirectTo(const crs::CRSNNPtr &targetCRS, Private::Context &context); static std::vector findsOpsInRegistryWithIntermediate( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, bool useCreateBetweenGeodeticCRSWithDatumBasedIntermediates); static void createOperationsFromProj4Ext( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector &res); static bool createOperationsFromDatabase( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res); static std::vector createOperationsGeogToVertFromGeoid(const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Context &context); static std::vector createOperationsGeogToVertWithIntermediateVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Context &context); static std::vector createOperationsGeogToVertWithAlternativeGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Context &context); static void createOperationsFromDatabaseWithVertCRS( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res); static void createOperationsGeodToGeod( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, std::vector &res); static void createOperationsDerivedTo( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::DerivedCRS *derivedSrc, std::vector &res); static void createOperationsBoundToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::GeographicCRS *geogDst, std::vector &res); static void createOperationsBoundToVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::VerticalCRS *vertDst, std::vector &res); static void createOperationsVertToVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res); static void createOperationsVertToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::VerticalCRS *vertSrc, const crs::GeographicCRS *geogDst, std::vector &res); static void createOperationsBoundToBound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector &res); static void createOperationsCompoundToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::GeographicCRS *geogDst, std::vector &res); static void createOperationsCompoundToGeod( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::GeodeticCRS *geodDst, std::vector &res); static void createOperationsCompoundToCompound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::CompoundCRS *compoundDst, std::vector &res); static void createOperationsBoundToCompound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::CompoundCRS *compoundDst, std::vector &res); static std::vector createOperationsGeogToGeog( std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst); static void createOperationsWithDatumPivot( std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, Context &context); static bool hasPerfectAccuracyResult(const std::vector &res, const Context &context); static void setCRSs(CoordinateOperation *co, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS); }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress CoordinateOperationFactory::~CoordinateOperationFactory() = default; //! @endcond // --------------------------------------------------------------------------- CoordinateOperationFactory::CoordinateOperationFactory() : d(nullptr) {} // --------------------------------------------------------------------------- /** \brief Find a CoordinateOperation from sourceCRS to targetCRS. * * This is a helper of createOperations(), using a coordinate operation * context * with no authority factory (so no catalog searching is done), no desired * accuracy and no area of interest. * This returns the first operation of the result set of createOperations(), * or null if none found. * * @param sourceCRS source CRS. * @param targetCRS source CRS. * @return a CoordinateOperation or nullptr. */ CoordinateOperationPtr CoordinateOperationFactory::createOperation( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) const { auto res = createOperations( sourceCRS, targetCRS, CoordinateOperationContext::create(nullptr, nullptr, 0.0)); if (!res.empty()) { return res[0]; } return nullptr; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- struct PrecomputedOpCharacteristics { double area_{}; double accuracy_{}; bool isPROJExportable_ = false; bool hasGrids_ = false; bool gridsAvailable_ = false; bool gridsKnown_ = false; size_t stepCount_ = 0; bool isApprox_ = false; bool isNullTransformation_ = false; PrecomputedOpCharacteristics() = default; PrecomputedOpCharacteristics(double area, double accuracy, bool isPROJExportable, bool hasGrids, bool gridsAvailable, bool gridsKnown, size_t stepCount, bool isApprox, bool isNullTransformation) : area_(area), accuracy_(accuracy), isPROJExportable_(isPROJExportable), hasGrids_(hasGrids), gridsAvailable_(gridsAvailable), gridsKnown_(gridsKnown), stepCount_(stepCount), isApprox_(isApprox), isNullTransformation_(isNullTransformation) {} }; // --------------------------------------------------------------------------- // We could have used a lambda instead of this old-school way, but // filterAndSort() is already huge. struct SortFunction { const std::map ↦ explicit SortFunction(const std::map &mapIn) : map(mapIn) {} // Sorting function // Return true if a < b bool operator()(const CoordinateOperationNNPtr &a, const CoordinateOperationNNPtr &b) const { auto iterA = map.find(a.get()); assert(iterA != map.end()); auto iterB = map.find(b.get()); assert(iterB != map.end()); // CAUTION: the order of the comparisons is extremely important // to get the intended result. if (iterA->second.isPROJExportable_ && !iterB->second.isPROJExportable_) { return true; } if (!iterA->second.isPROJExportable_ && iterB->second.isPROJExportable_) { return false; } if (!iterA->second.isApprox_ && iterB->second.isApprox_) { return true; } if (iterA->second.isApprox_ && !iterB->second.isApprox_) { return false; } if (!iterA->second.isNullTransformation_ && iterB->second.isNullTransformation_) { return true; } if (iterA->second.isNullTransformation_ && !iterB->second.isNullTransformation_) { return false; } // Operations where grids are all available go before other if (iterA->second.gridsAvailable_ && !iterB->second.gridsAvailable_) { return true; } if (iterB->second.gridsAvailable_ && !iterA->second.gridsAvailable_) { return false; } // Operations where grids are all known in our DB go before other if (iterA->second.gridsKnown_ && !iterB->second.gridsKnown_) { return true; } if (iterB->second.gridsKnown_ && !iterA->second.gridsKnown_) { return false; } // Operations with known accuracy go before those with unknown accuracy const double accuracyA = iterA->second.accuracy_; const double accuracyB = iterB->second.accuracy_; if (accuracyA >= 0 && accuracyB < 0) { return true; } if (accuracyB >= 0 && accuracyA < 0) { return false; } if (accuracyA < 0 && accuracyB < 0) { // unknown accuracy ? then prefer operations with grids, which // are likely to have best practical accuracy if (iterA->second.hasGrids_ && !iterB->second.hasGrids_) { return true; } if (!iterA->second.hasGrids_ && iterB->second.hasGrids_) { return false; } } // Operations with larger non-zero area of use go before those with // lower one const double areaA = iterA->second.area_; const double areaB = iterB->second.area_; if (areaA > 0) { if (areaA > areaB) { return true; } if (areaA < areaB) { return false; } } else if (areaB > 0) { return false; } // Operations with better accuracy go before those with worse one if (accuracyA >= 0 && accuracyA < accuracyB) { return true; } if (accuracyB >= 0 && accuracyB < accuracyA) { return false; } if (accuracyA >= 0 && accuracyA == accuracyB) { // same accuracy ? then prefer operations without grids if (!iterA->second.hasGrids_ && iterB->second.hasGrids_) { return true; } if (iterA->second.hasGrids_ && !iterB->second.hasGrids_) { return false; } } // The less intermediate steps, the better if (iterA->second.stepCount_ < iterB->second.stepCount_) { return true; } if (iterB->second.stepCount_ < iterA->second.stepCount_) { return false; } const auto &a_name = a->nameStr(); const auto &b_name = b->nameStr(); // The shorter name, the better ? if (a_name.size() < b_name.size()) { return true; } if (b_name.size() < a_name.size()) { return false; } // Arbitrary final criterion return a_name < b_name; } }; // --------------------------------------------------------------------------- static size_t getStepCount(const CoordinateOperationNNPtr &op) { auto concat = dynamic_cast(op.get()); size_t stepCount = 1; if (concat) { stepCount = concat->operations().size(); } return stepCount; } // --------------------------------------------------------------------------- struct FilterResults { FilterResults(const std::vector &sourceListIn, const CoordinateOperationContextNNPtr &contextIn, const metadata::ExtentPtr &extent1In, const metadata::ExtentPtr &extent2In, bool forceStrictContainmentTest) : sourceList(sourceListIn), context(contextIn), extent1(extent1In), extent2(extent2In), areaOfInterest(context->getAreaOfInterest()), desiredAccuracy(context->getDesiredAccuracy()), sourceAndTargetCRSExtentUse( context->getSourceAndTargetCRSExtentUse()) { computeAreaOfInterest(); filterOut(forceStrictContainmentTest); } FilterResults &andSort() { sort(); // And now that we have a sorted list, we can remove uninteresting // results // ... removeSyntheticNullTransforms(); if (context->getDiscardSuperseded()) removeUninterestingOps(); removeDuplicateOps(); removeSyntheticNullTransforms(); return *this; } // ---------------------------------------------------------------------- // cppcheck-suppress functionStatic const std::vector &getRes() { return res; } // ---------------------------------------------------------------------- private: const std::vector &sourceList; const CoordinateOperationContextNNPtr &context; const metadata::ExtentPtr &extent1; const metadata::ExtentPtr &extent2; metadata::ExtentPtr areaOfInterest; const double desiredAccuracy = context->getDesiredAccuracy(); const CoordinateOperationContext::SourceTargetCRSExtentUse sourceAndTargetCRSExtentUse; bool hasOpThatContainsAreaOfInterest = false; std::vector res{}; // ---------------------------------------------------------------------- void computeAreaOfInterest() { // Compute an area of interest from the CRS extent if the user did // not specify one if (!areaOfInterest) { if (sourceAndTargetCRSExtentUse == CoordinateOperationContext::SourceTargetCRSExtentUse:: INTERSECTION) { if (extent1 && extent2) { areaOfInterest = extent1->intersection(NN_NO_CHECK(extent2)); } } else if (sourceAndTargetCRSExtentUse == CoordinateOperationContext::SourceTargetCRSExtentUse:: SMALLEST) { if (extent1 && extent2) { if (getPseudoArea(extent1) < getPseudoArea(extent2)) { areaOfInterest = extent1; } else { areaOfInterest = extent2; } } else if (extent1) { areaOfInterest = extent1; } else { areaOfInterest = extent2; } } } } // --------------------------------------------------------------------------- void filterOut(bool forceStrictContainmentTest) { // Filter out operations that do not match the expected accuracy // and area of use. const auto spatialCriterion = forceStrictContainmentTest ? CoordinateOperationContext::SpatialCriterion:: STRICT_CONTAINMENT : context->getSpatialCriterion(); bool hasFoundOpWithExtent = false; for (const auto &op : sourceList) { if (desiredAccuracy != 0) { const double accuracy = getAccuracy(op); if (accuracy < 0 || accuracy > desiredAccuracy) { continue; } } if (areaOfInterest) { bool emptyIntersection = false; auto extent = getExtent(op, true, emptyIntersection); if (!extent) continue; hasFoundOpWithExtent = true; bool extentContains = extent->contains(NN_NO_CHECK(areaOfInterest)); if (extentContains) { if (!op->hasBallparkTransformation()) { hasOpThatContainsAreaOfInterest = true; } } if (spatialCriterion == CoordinateOperationContext::SpatialCriterion:: STRICT_CONTAINMENT && !extentContains) { continue; } if (spatialCriterion == CoordinateOperationContext::SpatialCriterion:: PARTIAL_INTERSECTION && !extent->intersects(NN_NO_CHECK(areaOfInterest))) { continue; } } else if (sourceAndTargetCRSExtentUse == CoordinateOperationContext::SourceTargetCRSExtentUse:: BOTH) { bool emptyIntersection = false; auto extent = getExtent(op, true, emptyIntersection); if (!extent) continue; hasFoundOpWithExtent = true; bool extentContainsExtent1 = !extent1 || extent->contains(NN_NO_CHECK(extent1)); bool extentContainsExtent2 = !extent2 || extent->contains(NN_NO_CHECK(extent2)); if (extentContainsExtent1 && extentContainsExtent2) { if (!op->hasBallparkTransformation()) { hasOpThatContainsAreaOfInterest = true; } } if (spatialCriterion == CoordinateOperationContext::SpatialCriterion:: STRICT_CONTAINMENT) { if (!extentContainsExtent1 || !extentContainsExtent2) { continue; } } else if (spatialCriterion == CoordinateOperationContext::SpatialCriterion:: PARTIAL_INTERSECTION) { bool extentIntersectsExtent1 = !extent1 || extent->intersects(NN_NO_CHECK(extent1)); bool extentIntersectsExtent2 = extent2 && extent->intersects(NN_NO_CHECK(extent2)); if (!extentIntersectsExtent1 || !extentIntersectsExtent2) { continue; } } } res.emplace_back(op); } // In case no operation has an extent and no result is found, // retain all initial operations that match accuracy criterion. if (res.empty() && !hasFoundOpWithExtent) { for (const auto &op : sourceList) { if (desiredAccuracy != 0) { const double accuracy = getAccuracy(op); if (accuracy < 0 || accuracy > desiredAccuracy) { continue; } } res.emplace_back(op); } } } // ---------------------------------------------------------------------- void sort() { // Precompute a number of parameters for each operation that will be // useful for the sorting. std::map map; const auto gridAvailabilityUse = context->getGridAvailabilityUse(); for (const auto &op : res) { bool dummy = false; auto extentOp = getExtent(op, true, dummy); double area = 0.0; if (extentOp) { if (areaOfInterest) { area = getPseudoArea( extentOp->intersection(NN_NO_CHECK(areaOfInterest))); } else if (extent1 && extent2) { auto x = extentOp->intersection(NN_NO_CHECK(extent1)); auto y = extentOp->intersection(NN_NO_CHECK(extent2)); area = getPseudoArea(x) + getPseudoArea(y) - ((x && y) ? getPseudoArea(x->intersection(NN_NO_CHECK(y))) : 0.0); } else if (extent1) { area = getPseudoArea( extentOp->intersection(NN_NO_CHECK(extent1))); } else if (extent2) { area = getPseudoArea( extentOp->intersection(NN_NO_CHECK(extent2))); } else { area = getPseudoArea(extentOp); } } bool hasGrids = false; bool gridsAvailable = true; bool gridsKnown = true; if (context->getAuthorityFactory()) { const auto gridsNeeded = op->gridsNeeded( context->getAuthorityFactory()->databaseContext()); for (const auto &gridDesc : gridsNeeded) { hasGrids = true; if (gridAvailabilityUse == CoordinateOperationContext::GridAvailabilityUse:: USE_FOR_SORTING && !gridDesc.available) { gridsAvailable = false; } if (gridDesc.packageName.empty() && !gridDesc.available) { gridsKnown = false; } } } const auto stepCount = getStepCount(op); const bool isApprox = op->nameStr().find(BALLPARK_VERTICAL_TRANSFORMATION_PREFIX) != std::string::npos; const bool isNullTransformation = op->nameStr().find(BALLPARK_GEOGRAPHIC_OFFSET) != std::string::npos || op->nameStr().find(NULL_GEOGRAPHIC_OFFSET) != std::string::npos || op->nameStr().find(NULL_GEOCENTRIC_TRANSLATION) != std::string::npos || op->nameStr().find(BALLPARK_GEOCENTRIC_TRANSLATION) != std::string::npos; bool isPROJExportable = false; auto formatter = io::PROJStringFormatter::create(); try { op->exportToPROJString(formatter.get()); // Grids might be missing, but at least this is something // PROJ could potentially process isPROJExportable = true; } catch (const std::exception &) { } map[op.get()] = PrecomputedOpCharacteristics( area, getAccuracy(op), isPROJExportable, hasGrids, gridsAvailable, gridsKnown, stepCount, isApprox, isNullTransformation); } // Sort ! SortFunction sortFunc(map); std::sort(res.begin(), res.end(), sortFunc); // Debug code to check consistency of the sort function #ifdef DEBUG_SORT constexpr bool debugSort = true; #elif !defined(NDEBUG) const bool debugSort = getenv("PROJ_DEBUG_SORT_FUNCT") != nullptr; #endif #if defined(DEBUG_SORT) || !defined(NDEBUG) if (debugSort) { const bool assertIfIssue = !(getenv("PROJ_DEBUG_SORT_FUNCT_ASSERT") != nullptr); for (size_t i = 0; i < res.size(); ++i) { for (size_t j = i + 1; j < res.size(); ++j) { if (sortFunc(res[j], res[i])) { #ifdef DEBUG_SORT std::cerr << "Sorting issue with entry " << i << "(" << res[i]->nameStr() << ") and " << j << "(" << res[j]->nameStr() << ")" << std::endl; #endif if (assertIfIssue) { assert(false); } } } } } #endif } // ---------------------------------------------------------------------- void removeSyntheticNullTransforms() { // If we have more than one result, and than the last result is the // default "Ballpark geographic offset" or "Ballpark geocentric // translation" // operations we have synthetized, and that at least one operation // has the desired area of interest, remove it as // all previous results are necessarily better if (hasOpThatContainsAreaOfInterest && res.size() > 1) { const std::string &name = res.back()->nameStr(); if (name.find(BALLPARK_GEOGRAPHIC_OFFSET) != std::string::npos || name.find(NULL_GEOGRAPHIC_OFFSET) != std::string::npos || name.find(NULL_GEOCENTRIC_TRANSLATION) != std::string::npos || name.find(BALLPARK_GEOCENTRIC_TRANSLATION) != std::string::npos) { std::vector resTemp; for (size_t i = 0; i < res.size() - 1; i++) { resTemp.emplace_back(res[i]); } res = std::move(resTemp); } } } // ---------------------------------------------------------------------- void removeUninterestingOps() { // Eliminate operations that bring nothing, ie for a given area of use, // do not keep operations that have greater accuracy. Actually we must // be a bit more subtle than that, and take into account grid // availability std::vector resTemp; metadata::ExtentPtr lastExtent; double lastAccuracy = -1; bool lastHasGrids = false; bool lastGridsAvailable = true; std::set> setOfSetOfGrids; size_t lastStepCount = 0; CoordinateOperationPtr lastOp; bool first = true; for (const auto &op : res) { const auto curAccuracy = getAccuracy(op); bool dummy = false; const auto curExtent = getExtent(op, true, dummy); bool curHasGrids = false; bool curGridsAvailable = true; std::set curSetOfGrids; const auto curStepCount = getStepCount(op); if (context->getAuthorityFactory()) { const auto gridsNeeded = op->gridsNeeded( context->getAuthorityFactory()->databaseContext()); for (const auto &gridDesc : gridsNeeded) { curHasGrids = true; curSetOfGrids.insert(gridDesc.shortName); if (!gridDesc.available) { curGridsAvailable = false; } } } if (first) { resTemp.emplace_back(op); lastHasGrids = curHasGrids; lastGridsAvailable = curGridsAvailable; first = false; } else { if (lastOp->_isEquivalentTo(op.get())) { continue; } const bool sameExtent = ((!curExtent && !lastExtent) || (curExtent && lastExtent && curExtent->contains(NN_NO_CHECK(lastExtent)) && lastExtent->contains(NN_NO_CHECK(curExtent)))); if (((curAccuracy > lastAccuracy && lastAccuracy >= 0) || (curAccuracy < 0 && lastAccuracy >= 0)) && sameExtent) { // If that set of grids has always been used for that // extent, // no need to add them again if (setOfSetOfGrids.find(curSetOfGrids) != setOfSetOfGrids.end()) { continue; } const bool sameNameOrEmptyName = ((!curExtent && !lastExtent) || (curExtent && lastExtent && !curExtent->description()->empty() && *(curExtent->description()) == *(lastExtent->description()))); // If we have already found a operation without grids for // that extent, no need to add any lower accuracy operation if (!lastHasGrids && sameNameOrEmptyName) { continue; } // If we had only operations involving grids, but one // past operation had available grids, no need to add // the new one. if (curHasGrids && curGridsAvailable && lastGridsAvailable) { continue; } } else if (curAccuracy == lastAccuracy && sameExtent) { if (curStepCount > lastStepCount) { continue; } } resTemp.emplace_back(op); if (sameExtent) { if (!curHasGrids) { lastHasGrids = false; } if (curGridsAvailable) { lastGridsAvailable = true; } } else { setOfSetOfGrids.clear(); lastHasGrids = curHasGrids; lastGridsAvailable = curGridsAvailable; } } lastOp = op.as_nullable(); lastStepCount = curStepCount; lastExtent = curExtent; lastAccuracy = curAccuracy; if (!curSetOfGrids.empty()) { setOfSetOfGrids.insert(curSetOfGrids); } } res = std::move(resTemp); } // ---------------------------------------------------------------------- // cppcheck-suppress functionStatic void removeDuplicateOps() { if (res.size() <= 1) { return; } // When going from EPSG:4807 (NTF Paris) to EPSG:4171 (RGC93), we get // EPSG:7811, NTF (Paris) to RGF93 (2), 1 m // and unknown id, NTF (Paris) to NTF (1) + Inverse of RGF93 to NTF (2), // 1 m // both have same PROJ string and extent // Do not keep the later (that has more steps) as it adds no value. std::set setPROJPlusExtent; std::vector resTemp; for (const auto &op : res) { auto formatter = io::PROJStringFormatter::create(); try { std::string key(op->exportToPROJString(formatter.get())); bool dummy = false; auto extentOp = getExtent(op, true, dummy); if (extentOp) { const auto &geogElts = extentOp->geographicElements(); if (geogElts.size() == 1) { auto bbox = dynamic_cast< const metadata::GeographicBoundingBox *>( geogElts[0].get()); if (bbox) { double w = bbox->westBoundLongitude(); double s = bbox->southBoundLatitude(); double e = bbox->eastBoundLongitude(); double n = bbox->northBoundLatitude(); key += "-"; key += toString(w); key += "-"; key += toString(s); key += "-"; key += toString(e); key += "-"; key += toString(n); } } } if (setPROJPlusExtent.find(key) == setPROJPlusExtent.end()) { resTemp.emplace_back(op); setPROJPlusExtent.insert(key); } } catch (const std::exception &) { resTemp.emplace_back(op); } } res = std::move(resTemp); } }; // --------------------------------------------------------------------------- /** \brief Filter operations and sort them given context. * * If a desired accuracy is specified, only keep operations whose accuracy * is at least the desired one. * If an area of interest is specified, only keep operations whose area of * use include the area of interest. * Then sort remaining operations by descending area of use, and increasing * accuracy. */ static std::vector filterAndSort(const std::vector &sourceList, const CoordinateOperationContextNNPtr &context, const metadata::ExtentPtr &extent1, const metadata::ExtentPtr &extent2) { #ifdef TRACE_CREATE_OPERATIONS ENTER_FUNCTION(); logTrace("number of results before filter and sort: " + toString(static_cast(sourceList.size()))); #endif auto resFiltered = FilterResults(sourceList, context, extent1, extent2, false) .andSort() .getRes(); #ifdef TRACE_CREATE_OPERATIONS logTrace("number of results after filter and sort: " + toString(static_cast(resFiltered.size()))); #endif return resFiltered; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // Apply the inverse() method on all elements of the input list static std::vector applyInverse(const std::vector &list) { auto res = list; for (auto &op : res) { #ifdef DEBUG auto opNew = op->inverse(); assert(opNew->targetCRS()->isEquivalentTo(op->sourceCRS().get())); assert(opNew->sourceCRS()->isEquivalentTo(op->targetCRS().get())); op = opNew; #else op = op->inverse(); #endif } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void CoordinateOperationFactory::Private::buildCRSIds( const crs::CRSNNPtr &crs, Private::Context &context, std::list> &ids) { for (const auto &id : crs->identifiers()) { const auto &authName = *(id->codeSpace()); const auto &code = id->code(); if (!authName.empty()) { ids.emplace_back(authName, code); } } if (ids.empty()) { std::vector allowedObjects; auto geogCRS = dynamic_cast(crs.get()); if (geogCRS) { allowedObjects.push_back( geogCRS->coordinateSystem()->axisList().size() == 2 ? io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS : io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); } else if (dynamic_cast(crs.get())) { allowedObjects.push_back( io::AuthorityFactory::ObjectType::PROJECTED_CRS); } else if (dynamic_cast(crs.get())) { allowedObjects.push_back( io::AuthorityFactory::ObjectType::VERTICAL_CRS); } if (!allowedObjects.empty()) { const std::pair key( allowedObjects[0], crs->nameStr()); auto iter = context.cacheNameToCRS.find(key); if (iter != context.cacheNameToCRS.end()) { ids = iter->second; return; } const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); const auto &authFactoryName = authFactory->getAuthority(); try { const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), (authFactoryName.empty() || authFactoryName == "any") ? std::string() : authFactoryName); auto matches = tmpAuthFactory->createObjectsFromName( crs->nameStr(), allowedObjects, false, 2); if (matches.size() == 1 && crs->_isEquivalentTo( matches.front().get(), util::IComparable::Criterion::EQUIVALENT) && !matches.front()->identifiers().empty()) { const auto &tmpIds = matches.front()->identifiers(); ids.emplace_back(*(tmpIds[0]->codeSpace()), tmpIds[0]->code()); } } catch (const std::exception &) { } context.cacheNameToCRS[key] = ids; } } } // --------------------------------------------------------------------------- static std::vector getCandidateAuthorities(const io::AuthorityFactoryPtr &authFactory, const std::string &srcAuthName, const std::string &targetAuthName) { const auto &authFactoryName = authFactory->getAuthority(); std::vector authorities; if (authFactoryName == "any") { authorities.emplace_back(); } if (authFactoryName.empty()) { authorities = authFactory->databaseContext()->getAllowedAuthorities( srcAuthName, targetAuthName); if (authorities.empty()) { authorities.emplace_back(); } } else { authorities.emplace_back(authFactoryName); } return authorities; } // --------------------------------------------------------------------------- // Look in the authority registry for operations from sourceCRS to targetCRS std::vector CoordinateOperationFactory::Private::findOpsInRegistryDirect( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, bool &resNonEmptyBeforeFiltering) { const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("findOpsInRegistryDirect(" + objectAsStr(sourceCRS.get()) + " --> " + objectAsStr(targetCRS.get()) + ")"); #endif resNonEmptyBeforeFiltering = false; std::list> sourceIds; std::list> targetIds; buildCRSIds(sourceCRS, context, sourceIds); buildCRSIds(targetCRS, context, targetIds); for (const auto &idSrc : sourceIds) { const auto &srcAuthName = idSrc.first; const auto &srcCode = idSrc.second; for (const auto &idTarget : targetIds) { const auto &targetAuthName = idTarget.first; const auto &targetCode = idTarget.second; const auto authorities(getCandidateAuthorities( authFactory, srcAuthName, targetAuthName)); std::vector res; for (const auto &authority : authorities) { const auto authName = authority == "any" ? std::string() : authority; const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), authName); auto resTmp = tmpAuthFactory->createFromCoordinateReferenceSystemCodes( srcAuthName, srcCode, targetAuthName, targetCode, context.context->getUsePROJAlternativeGridNames(), context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, context.context->getDiscardSuperseded(), true, false, context.extent1, context.extent2); res.insert(res.end(), resTmp.begin(), resTmp.end()); if (authName == "PROJ") { continue; } if (!res.empty()) { resNonEmptyBeforeFiltering = true; auto resFiltered = FilterResults(res, context.context, context.extent1, context.extent2, false) .getRes(); #ifdef TRACE_CREATE_OPERATIONS logTrace("filtering reduced from " + toString(static_cast(res.size())) + " to " + toString(static_cast(resFiltered.size()))); #endif return resFiltered; } } } } return std::vector(); } // --------------------------------------------------------------------------- // Look in the authority registry for operations to targetCRS std::vector CoordinateOperationFactory::Private::findOpsInRegistryDirectTo( const crs::CRSNNPtr &targetCRS, Private::Context &context) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("findOpsInRegistryDirectTo({any} -->" + objectAsStr(targetCRS.get()) + ")"); #endif const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); std::list> ids; buildCRSIds(targetCRS, context, ids); for (const auto &id : ids) { const auto &targetAuthName = id.first; const auto &targetCode = id.second; const auto authorities(getCandidateAuthorities( authFactory, targetAuthName, targetAuthName)); for (const auto &authority : authorities) { const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), authority == "any" ? std::string() : authority); auto res = tmpAuthFactory->createFromCoordinateReferenceSystemCodes( std::string(), std::string(), targetAuthName, targetCode, context.context->getUsePROJAlternativeGridNames(), context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, context.context->getDiscardSuperseded(), true, true, context.extent1, context.extent2); if (!res.empty()) { auto resFiltered = FilterResults(res, context.context, context.extent1, context.extent2, false) .getRes(); #ifdef TRACE_CREATE_OPERATIONS logTrace("filtering reduced from " + toString(static_cast(res.size())) + " to " + toString(static_cast(resFiltered.size()))); #endif return resFiltered; } } } return std::vector(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // Look in the authority registry for operations from sourceCRS to targetCRS // using an intermediate pivot std::vector CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, bool useCreateBetweenGeodeticCRSWithDatumBasedIntermediates) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("findsOpsInRegistryWithIntermediate(" + objectAsStr(sourceCRS.get()) + " --> " + objectAsStr(targetCRS.get()) + ")"); #endif const auto &authFactory = context.context->getAuthorityFactory(); assert(authFactory); std::list> sourceIds; std::list> targetIds; buildCRSIds(sourceCRS, context, sourceIds); buildCRSIds(targetCRS, context, targetIds); for (const auto &idSrc : sourceIds) { const auto &srcAuthName = idSrc.first; const auto &srcCode = idSrc.second; for (const auto &idTarget : targetIds) { const auto &targetAuthName = idTarget.first; const auto &targetCode = idTarget.second; const auto authorities(getCandidateAuthorities( authFactory, srcAuthName, targetAuthName)); assert(!authorities.empty()); const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), (authFactory->getAuthority() == "any" || authorities.size() > 1) ? std::string() : authorities.front()); std::vector res; if (useCreateBetweenGeodeticCRSWithDatumBasedIntermediates) { res = tmpAuthFactory ->createBetweenGeodeticCRSWithDatumBasedIntermediates( sourceCRS, srcAuthName, srcCode, targetCRS, targetAuthName, targetCode, context.context->getUsePROJAlternativeGridNames(), context.context->getGridAvailabilityUse() == CoordinateOperationContext:: GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, context.context->getDiscardSuperseded(), authFactory->getAuthority() != "any" && authorities.size() > 1 ? authorities : std::vector(), context.extent1, context.extent2); } else { io::AuthorityFactory::ObjectType intermediateObjectType = io::AuthorityFactory::ObjectType::CRS; // If doing GeogCRS --> GeogCRS, only use GeogCRS as // intermediate CRS // Avoid weird behaviour when doing NAD83 -> NAD83(2011) // that would go through NAVD88 otherwise. if (context.context->getIntermediateCRS().empty() && dynamic_cast(sourceCRS.get()) && dynamic_cast(targetCRS.get())) { intermediateObjectType = io::AuthorityFactory::ObjectType::GEOGRAPHIC_CRS; } res = tmpAuthFactory->createFromCRSCodesWithIntermediates( srcAuthName, srcCode, targetAuthName, targetCode, context.context->getUsePROJAlternativeGridNames(), context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: DISCARD_OPERATION_IF_MISSING_GRID, context.context->getDiscardSuperseded(), context.context->getIntermediateCRS(), intermediateObjectType, authFactory->getAuthority() != "any" && authorities.size() > 1 ? authorities : std::vector(), context.extent1, context.extent2); } if (!res.empty()) { auto resFiltered = FilterResults(res, context.context, context.extent1, context.extent2, false) .getRes(); #ifdef TRACE_CREATE_OPERATIONS logTrace("filtering reduced from " + toString(static_cast(res.size())) + " to " + toString(static_cast(resFiltered.size()))); #endif return resFiltered; } } } return std::vector(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static TransformationNNPtr createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { const crs::GeographicCRS *geogSrc = dynamic_cast(sourceCRS.get()); const crs::GeographicCRS *geogDst = dynamic_cast(targetCRS.get()); const bool isSameDatum = geogSrc && geogDst && geogSrc->datum() && geogDst->datum() && geogSrc->datum()->_isEquivalentTo( geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT); auto name = buildOpName(isSameDatum ? NULL_GEOGRAPHIC_OFFSET : BALLPARK_GEOGRAPHIC_OFFSET, sourceCRS, targetCRS); const auto &sourceCRSExtent = getExtent(sourceCRS); const auto &targetCRSExtent = getExtent(targetCRS); const bool sameExtent = sourceCRSExtent && targetCRSExtent && sourceCRSExtent->_isEquivalentTo( targetCRSExtent.get(), util::IComparable::Criterion::EQUIVALENT); util::PropertyMap map; map.set(common::IdentifiedObject::NAME_KEY, name) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, sameExtent ? NN_NO_CHECK(sourceCRSExtent) : metadata::Extent::WORLD); const common::Angle angle0(0); std::vector accuracies; if (isSameDatum) { accuracies.emplace_back(metadata::PositionalAccuracy::create("0")); } if (dynamic_cast(sourceCRS.get()) ->coordinateSystem() ->axisList() .size() == 3 || dynamic_cast(targetCRS.get()) ->coordinateSystem() ->axisList() .size() == 3) { return Transformation::createGeographic3DOffsets( map, sourceCRS, targetCRS, angle0, angle0, common::Length(0), accuracies); } else { return Transformation::createGeographic2DOffsets( map, sourceCRS, targetCRS, angle0, angle0, accuracies); } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct MyPROJStringExportableGeodToGeod final : public io::IPROJStringExportable { crs::GeodeticCRSPtr geodSrc{}; crs::GeodeticCRSPtr geodDst{}; MyPROJStringExportableGeodToGeod(const crs::GeodeticCRSPtr &geodSrcIn, const crs::GeodeticCRSPtr &geodDstIn) : geodSrc(geodSrcIn), geodDst(geodDstIn) {} ~MyPROJStringExportableGeodToGeod() override; void // cppcheck-suppress functionStatic _exportToPROJString(io::PROJStringFormatter *formatter) const override { formatter->startInversion(); geodSrc->_exportToPROJString(formatter); formatter->stopInversion(); geodDst->_exportToPROJString(formatter); } }; MyPROJStringExportableGeodToGeod::~MyPROJStringExportableGeodToGeod() = default; // --------------------------------------------------------------------------- struct MyPROJStringExportableHorizVertical final : public io::IPROJStringExportable { CoordinateOperationPtr horizTransform{}; CoordinateOperationPtr verticalTransform{}; crs::GeographicCRSPtr geogDst{}; MyPROJStringExportableHorizVertical( const CoordinateOperationPtr &horizTransformIn, const CoordinateOperationPtr &verticalTransformIn, const crs::GeographicCRSPtr &geogDstIn) : horizTransform(horizTransformIn), verticalTransform(verticalTransformIn), geogDst(geogDstIn) {} ~MyPROJStringExportableHorizVertical() override; void // cppcheck-suppress functionStatic _exportToPROJString(io::PROJStringFormatter *formatter) const override { formatter->pushOmitZUnitConversion(); horizTransform->_exportToPROJString(formatter); formatter->startInversion(); geogDst->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); formatter->popOmitZUnitConversion(); verticalTransform->_exportToPROJString(formatter); formatter->pushOmitZUnitConversion(); geogDst->addAngularUnitConvertAndAxisSwap(formatter); formatter->popOmitZUnitConversion(); } }; MyPROJStringExportableHorizVertical::~MyPROJStringExportableHorizVertical() = default; // --------------------------------------------------------------------------- struct MyPROJStringExportableHorizVerticalHorizPROJBased final : public io::IPROJStringExportable { CoordinateOperationPtr opSrcCRSToGeogCRS{}; CoordinateOperationPtr verticalTransform{}; CoordinateOperationPtr opGeogCRStoDstCRS{}; crs::GeographicCRSPtr interpolationGeogCRS{}; MyPROJStringExportableHorizVerticalHorizPROJBased( const CoordinateOperationPtr &opSrcCRSToGeogCRSIn, const CoordinateOperationPtr &verticalTransformIn, const CoordinateOperationPtr &opGeogCRStoDstCRSIn, const crs::GeographicCRSPtr &interpolationGeogCRSIn) : opSrcCRSToGeogCRS(opSrcCRSToGeogCRSIn), verticalTransform(verticalTransformIn), opGeogCRStoDstCRS(opGeogCRStoDstCRSIn), interpolationGeogCRS(interpolationGeogCRSIn) {} ~MyPROJStringExportableHorizVerticalHorizPROJBased() override; void // cppcheck-suppress functionStatic _exportToPROJString(io::PROJStringFormatter *formatter) const override { formatter->pushOmitZUnitConversion(); opSrcCRSToGeogCRS->_exportToPROJString(formatter); formatter->startInversion(); interpolationGeogCRS->addAngularUnitConvertAndAxisSwap(formatter); formatter->stopInversion(); formatter->popOmitZUnitConversion(); verticalTransform->_exportToPROJString(formatter); formatter->pushOmitZUnitConversion(); interpolationGeogCRS->addAngularUnitConvertAndAxisSwap(formatter); opGeogCRStoDstCRS->_exportToPROJString(formatter); formatter->popOmitZUnitConversion(); } }; MyPROJStringExportableHorizVerticalHorizPROJBased:: ~MyPROJStringExportableHorizVerticalHorizPROJBased() = default; //! @endcond } // namespace operation NS_PROJ_END #if 0 namespace dropbox{ namespace oxygen { template<> nn>::~nn() = default; template<> nn>::~nn() = default; template<> nn>::~nn() = default; }} #endif NS_PROJ_START namespace operation { //! @cond Doxygen_Suppress // --------------------------------------------------------------------------- static std::string buildTransfName(const std::string &srcName, const std::string &targetName) { std::string name("Transformation from "); name += srcName; name += " to "; name += targetName; return name; } // --------------------------------------------------------------------------- static CoordinateOperationNNPtr createGeodToGeodPROJBased(const crs::CRSNNPtr &geodSrc, const crs::CRSNNPtr &geodDst) { auto exportable = util::nn_make_shared( util::nn_dynamic_pointer_cast(geodSrc), util::nn_dynamic_pointer_cast(geodDst)); auto properties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildTransfName(geodSrc->nameStr(), geodDst->nameStr())); return createPROJBased(properties, exportable, geodSrc, geodDst, {}, false); } // --------------------------------------------------------------------------- static CoordinateOperationNNPtr createHorizVerticalPROJBased( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const operation::CoordinateOperationNNPtr &horizTransform, const operation::CoordinateOperationNNPtr &verticalTransform) { auto geogDst = util::nn_dynamic_pointer_cast(targetCRS); assert(geogDst); auto exportable = util::nn_make_shared( horizTransform, verticalTransform, geogDst); const bool horizTransformIsNoOp = starts_with(horizTransform->nameStr(), NULL_GEOGRAPHIC_OFFSET); if (horizTransformIsNoOp) { auto properties = util::PropertyMap(); properties.set(common::IdentifiedObject::NAME_KEY, verticalTransform->nameStr()); bool dummy = false; auto extent = getExtent(verticalTransform, true, dummy); if (extent) { properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(extent)); } return createPROJBased( properties, exportable, sourceCRS, targetCRS, verticalTransform->coordinateOperationAccuracies(), verticalTransform->hasBallparkTransformation()); } else { bool dummy = false; auto ops = std::vector{horizTransform, verticalTransform}; auto extent = getExtent(ops, true, dummy); auto properties = util::PropertyMap(); properties.set(common::IdentifiedObject::NAME_KEY, computeConcatenatedName(ops)); if (extent) { properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(extent)); } std::vector accuracies; const double accuracy = getAccuracy(ops); if (accuracy >= 0.0) { accuracies.emplace_back( metadata::PositionalAccuracy::create(toString(accuracy))); } return createPROJBased( properties, exportable, sourceCRS, targetCRS, accuracies, horizTransform->hasBallparkTransformation() || verticalTransform->hasBallparkTransformation()); } } // --------------------------------------------------------------------------- static CoordinateOperationNNPtr createHorizVerticalHorizPROJBased( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const operation::CoordinateOperationNNPtr &opSrcCRSToGeogCRS, const operation::CoordinateOperationNNPtr &verticalTransform, const operation::CoordinateOperationNNPtr &opGeogCRStoDstCRS, const crs::GeographicCRSPtr &interpolationGeogCRS, bool checkExtent) { auto exportable = util::nn_make_shared( opSrcCRSToGeogCRS, verticalTransform, opGeogCRStoDstCRS, interpolationGeogCRS); std::vector ops; if (!starts_with(opSrcCRSToGeogCRS->nameStr(), NULL_GEOGRAPHIC_OFFSET)) { ops.emplace_back(opSrcCRSToGeogCRS); } ops.emplace_back(verticalTransform); if (!starts_with(opGeogCRStoDstCRS->nameStr(), NULL_GEOGRAPHIC_OFFSET)) { ops.emplace_back(opGeogCRStoDstCRS); } bool hasBallparkTransformation = false; for (const auto &op : ops) { hasBallparkTransformation |= op->hasBallparkTransformation(); } bool emptyIntersection = false; auto extent = getExtent(ops, false, emptyIntersection); if (checkExtent && emptyIntersection) { std::string msg( "empty intersection of area of validity of concatenated " "operations"); throw InvalidOperationEmptyIntersection(msg); } auto properties = util::PropertyMap(); properties.set(common::IdentifiedObject::NAME_KEY, computeConcatenatedName(ops)); if (extent) { properties.set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(extent)); } std::vector accuracies; const double accuracy = getAccuracy(ops); if (accuracy >= 0.0) { accuracies.emplace_back( metadata::PositionalAccuracy::create(toString(accuracy))); } return createPROJBased(properties, exportable, sourceCRS, targetCRS, accuracies, hasBallparkTransformation); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::vector CoordinateOperationFactory::Private::createOperationsGeogToGeog( std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst) { assert(sourceCRS.get() == geogSrc); assert(targetCRS.get() == geogDst); const auto &src_pm = geogSrc->primeMeridian()->longitude(); const auto &dst_pm = geogDst->primeMeridian()->longitude(); auto offset_pm = (src_pm.unit() == dst_pm.unit()) ? common::Angle(src_pm.value() - dst_pm.value(), src_pm.unit()) : common::Angle( src_pm.convertToUnit(common::UnitOfMeasure::DEGREE) - dst_pm.convertToUnit(common::UnitOfMeasure::DEGREE), common::UnitOfMeasure::DEGREE); double vconvSrc = 1.0; const auto &srcCS = geogSrc->coordinateSystem(); const auto &srcAxisList = srcCS->axisList(); if (srcAxisList.size() == 3) { vconvSrc = srcAxisList[2]->unit().conversionToSI(); } double vconvDst = 1.0; const auto &dstCS = geogDst->coordinateSystem(); const auto &dstAxisList = dstCS->axisList(); if (dstAxisList.size() == 3) { vconvDst = dstAxisList[2]->unit().conversionToSI(); } std::string name(buildTransfName(geogSrc->nameStr(), geogDst->nameStr())); const bool sameDatum = geogSrc->datum() != nullptr && geogDst->datum() != nullptr && geogSrc->datum()->_isEquivalentTo( geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT); // Do the CRS differ by their axis order ? bool axisReversal2D = false; bool axisReversal3D = false; if (!srcCS->_isEquivalentTo(dstCS.get(), util::IComparable::Criterion::EQUIVALENT)) { auto srcOrder = srcCS->axisOrder(); auto dstOrder = dstCS->axisOrder(); if (((srcOrder == cs::EllipsoidalCS::AxisOrder::LAT_NORTH_LONG_EAST || srcOrder == cs::EllipsoidalCS::AxisOrder:: LAT_NORTH_LONG_EAST_HEIGHT_UP) && (dstOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH || dstOrder == cs::EllipsoidalCS::AxisOrder:: LONG_EAST_LAT_NORTH_HEIGHT_UP)) || ((srcOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH || srcOrder == cs::EllipsoidalCS::AxisOrder:: LONG_EAST_LAT_NORTH_HEIGHT_UP) && (dstOrder == cs::EllipsoidalCS::AxisOrder::LAT_NORTH_LONG_EAST || dstOrder == cs::EllipsoidalCS::AxisOrder:: LAT_NORTH_LONG_EAST_HEIGHT_UP))) { if (srcAxisList.size() == 3 || dstAxisList.size() == 3) axisReversal3D = true; else axisReversal2D = true; } } // Do they differ by vertical units ? if (vconvSrc != vconvDst && geogSrc->ellipsoid()->_isEquivalentTo( geogDst->ellipsoid().get(), util::IComparable::Criterion::EQUIVALENT)) { if (offset_pm.value() == 0 && !axisReversal2D && !axisReversal3D) { // If only by vertical units, use a Change of Vertical // Unit // transformation const double factor = vconvSrc / vconvDst; auto conv = Conversion::createChangeVerticalUnit( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name), common::Scale(factor)); conv->setCRSs(sourceCRS, targetCRS, nullptr); conv->setHasBallparkTransformation(!sameDatum); res.push_back(conv); return res; } else { auto op = createGeodToGeodPROJBased(sourceCRS, targetCRS); op->setHasBallparkTransformation(!sameDatum); res.emplace_back(op); return res; } } // Do the CRS differ only by their axis order ? if (sameDatum && (axisReversal2D || axisReversal3D)) { auto conv = Conversion::createAxisOrderReversal(axisReversal3D); conv->setCRSs(sourceCRS, targetCRS, nullptr); res.emplace_back(conv); return res; } std::vector steps; // If both are geographic and only differ by their prime // meridian, // apply a longitude rotation transformation. if (geogSrc->ellipsoid()->_isEquivalentTo( geogDst->ellipsoid().get(), util::IComparable::Criterion::EQUIVALENT) && src_pm.getSIValue() != dst_pm.getSIValue()) { steps.emplace_back(Transformation::createLongitudeRotation( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), sourceCRS, targetCRS, offset_pm)); // If only the target has a non-zero prime meridian, chain a // null geographic offset and then the longitude rotation } else if (src_pm.getSIValue() == 0 && dst_pm.getSIValue() != 0) { auto datum = datum::GeodeticReferenceFrame::create( util::PropertyMap(), geogDst->ellipsoid(), util::optional(), geogSrc->primeMeridian()); std::string interm_crs_name(geogDst->nameStr()); interm_crs_name += " altered to use prime meridian of "; interm_crs_name += geogSrc->nameStr(); auto interm_crs = util::nn_static_pointer_cast(crs::GeographicCRS::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, interm_crs_name) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), datum, dstCS)); steps.emplace_back( createBallparkGeographicOffset(sourceCRS, interm_crs)); steps.emplace_back(Transformation::createLongitudeRotation( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, buildTransfName(geogSrc->nameStr(), interm_crs->nameStr())) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), interm_crs, targetCRS, offset_pm)); } else { // If the prime meridians are different, chain a longitude // rotation and the null geographic offset. if (src_pm.getSIValue() != dst_pm.getSIValue()) { auto datum = datum::GeodeticReferenceFrame::create( util::PropertyMap(), geogSrc->ellipsoid(), util::optional(), geogDst->primeMeridian()); std::string interm_crs_name(geogSrc->nameStr()); interm_crs_name += " altered to use prime meridian of "; interm_crs_name += geogDst->nameStr(); auto interm_crs = util::nn_static_pointer_cast( crs::GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, interm_crs_name), datum, srcCS)); steps.emplace_back(Transformation::createLongitudeRotation( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, buildTransfName(geogSrc->nameStr(), interm_crs->nameStr())) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), sourceCRS, interm_crs, offset_pm)); steps.emplace_back( createBallparkGeographicOffset(interm_crs, targetCRS)); } else { steps.emplace_back( createBallparkGeographicOffset(sourceCRS, targetCRS)); } } auto op = ConcatenatedOperation::createComputeMetadata( steps, !allowEmptyIntersection); op->setHasBallparkTransformation(!sameDatum); res.emplace_back(op); return res; } // --------------------------------------------------------------------------- static bool hasIdentifiers(const CoordinateOperationNNPtr &op) { if (!op->identifiers().empty()) { return true; } auto concatenated = dynamic_cast(op.get()); if (concatenated) { for (const auto &subOp : concatenated->operations()) { if (hasIdentifiers(subOp)) { return true; } } } return false; } // --------------------------------------------------------------------------- static std::vector findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory, const crs::GeodeticCRS *crs, const datum::GeodeticReferenceFrame *datum) { std::vector candidates; assert(datum); const auto &ids = datum->identifiers(); const auto &datumName = datum->nameStr(); if (!ids.empty()) { for (const auto &id : ids) { const auto &authName = *(id->codeSpace()); const auto &code = id->code(); if (!authName.empty()) { const auto crsIds = crs->identifiers(); const auto tmpFactory = (crsIds.size() == 1 && *(crsIds.front()->codeSpace()) == authName) ? io::AuthorityFactory::create( authFactory->databaseContext(), authName) .as_nullable() : authFactory; auto l_candidates = tmpFactory->createGeodeticCRSFromDatum( authName, code, std::string()); for (const auto &candidate : l_candidates) { candidates.emplace_back(candidate); } } } } else if (datumName != "unknown" && datumName != "unnamed") { auto matches = authFactory->createObjectsFromName( datumName, {io::AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, false, 2); if (matches.size() == 1) { const auto &match = matches.front(); if (datum->_isEquivalentTo( match.get(), util::IComparable::Criterion::EQUIVALENT) && !match->identifiers().empty()) { return findCandidateGeodCRSForDatum( authFactory, crs, dynamic_cast( match.get())); } } } return candidates; } // --------------------------------------------------------------------------- static bool isNullTransformation(const std::string &name) { return starts_with(name, BALLPARK_GEOCENTRIC_TRANSLATION) || starts_with(name, BALLPARK_GEOGRAPHIC_OFFSET) || starts_with(name, NULL_GEOGRAPHIC_OFFSET) || starts_with(name, NULL_GEOCENTRIC_TRANSLATION); } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::setCRSs( CoordinateOperation *co, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { co->setCRSs(sourceCRS, targetCRS, nullptr); auto invCO = dynamic_cast(co); if (invCO) { invCO->forwardOperation()->setCRSs(targetCRS, sourceCRS, nullptr); } auto transf = dynamic_cast(co); if (transf) { transf->inverseAsTransformation()->setCRSs(targetCRS, sourceCRS, nullptr); } auto concat = dynamic_cast(co); if (concat) { auto first = concat->operations().front().get(); auto &firstTarget(first->targetCRS()); if (firstTarget) { setCRSs(first, sourceCRS, NN_NO_CHECK(firstTarget)); } auto last = concat->operations().back().get(); auto &lastSource(last->sourceCRS()); if (lastSource) { setCRSs(last, NN_NO_CHECK(lastSource), targetCRS); } } } // --------------------------------------------------------------------------- static bool hasResultSetOnlyResultsWithPROJStep( const std::vector &res) { for (const auto &op : res) { auto concat = dynamic_cast(op.get()); if (concat) { bool hasPROJStep = false; const auto &steps = concat->operations(); for (const auto &step : steps) { const auto &ids = step->identifiers(); if (!ids.empty()) { const auto &opAuthority = *(ids.front()->codeSpace()); if (opAuthority == "PROJ" || opAuthority == "INVERSE(PROJ)" || opAuthority == "DERIVED_FROM(PROJ)") { hasPROJStep = true; break; } } } if (!hasPROJStep) { return false; } } else { return false; } } return true; } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, Private::Context &context) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("createOperationsWithDatumPivot(" + objectAsStr(sourceCRS.get()) + "," + objectAsStr(targetCRS.get()) + ")"); #endif struct CreateOperationsWithDatumPivotAntiRecursion { Context &context; explicit CreateOperationsWithDatumPivotAntiRecursion(Context &contextIn) : context(contextIn) { assert(!context.inCreateOperationsWithDatumPivotAntiRecursion); context.inCreateOperationsWithDatumPivotAntiRecursion = true; } ~CreateOperationsWithDatumPivotAntiRecursion() { context.inCreateOperationsWithDatumPivotAntiRecursion = false; } }; CreateOperationsWithDatumPivotAntiRecursion guard(context); const auto &authFactory = context.context->getAuthorityFactory(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( authFactory, geodSrc, geodSrc->datum().get())); const auto candidatesDstGeod(findCandidateGeodCRSForDatum( authFactory, geodDst, geodDst->datum().get())); auto createTransformations = [&](const crs::CRSNNPtr &candidateSrcGeod, const crs::CRSNNPtr &candidateDstGeod, const CoordinateOperationNNPtr &opFirst, bool isNullFirst) { const auto opsSecond = createOperations(candidateSrcGeod, candidateDstGeod, context); const auto opsThird = createOperations(candidateDstGeod, targetCRS, context); assert(!opsThird.empty()); for (auto &opSecond : opsSecond) { // Check that it is not a transformation synthetized by // ourselves if (!hasIdentifiers(opSecond)) { continue; } // And even if it is a referenced transformation, check that // it is not a trivial one auto so = dynamic_cast(opSecond.get()); if (so && isAxisOrderReversal(so->method()->getEPSGCode())) { continue; } std::vector subOps; const bool isNullThird = isNullTransformation(opsThird[0]->nameStr()); CoordinateOperationNNPtr opSecondCloned( (isNullFirst || isNullThird) ? opSecond->shallowClone() : opSecond); CoordinateOperation *invCOForward = nullptr; if (isNullFirst || isNullThird) { if (opSecondCloned->identifiers().size() == 1 && (*opSecondCloned->identifiers()[0]->codeSpace()) .find("DERIVED_FROM") == std::string::npos) { { util::PropertyMap map; addModifiedIdentifier(map, opSecondCloned.get(), false, true); opSecondCloned->setProperties(map); } auto invCO = dynamic_cast( opSecondCloned.get()); if (invCO) { invCOForward = invCO->forwardOperation().get(); if (invCOForward->identifiers().size() == 1 && (*invCOForward->identifiers()[0]->codeSpace()) .find("DERIVED_FROM") == std::string::npos) { util::PropertyMap map; addModifiedIdentifier(map, invCOForward, false, true); invCOForward->setProperties(map); } } } } if (isNullFirst) { auto oldTarget(NN_CHECK_ASSERT(opSecondCloned->targetCRS())); setCRSs(opSecondCloned.get(), sourceCRS, oldTarget); if (invCOForward) { setCRSs(invCOForward, oldTarget, sourceCRS); } } else { subOps.emplace_back(opFirst); } if (isNullThird) { auto oldSource(NN_CHECK_ASSERT(opSecondCloned->sourceCRS())); setCRSs(opSecondCloned.get(), oldSource, targetCRS); if (invCOForward) { setCRSs(invCOForward, targetCRS, oldSource); } subOps.emplace_back(opSecondCloned); } else { subOps.emplace_back(opSecondCloned); subOps.emplace_back(opsThird[0]); } #ifdef TRACE_CREATE_OPERATIONS std::string debugStr; for (const auto &op : subOps) { if (!debugStr.empty()) { debugStr += " + "; } debugStr += objectAsStr(op.get()); debugStr += " ("; debugStr += objectAsStr(op->sourceCRS().get()); debugStr += "->"; debugStr += objectAsStr(op->targetCRS().get()); debugStr += ")"; } logTrace("transformation " + debugStr); #endif res.emplace_back(ConcatenatedOperation::createComputeMetadata( subOps, !allowEmptyIntersection)); } }; // Start in priority with candidates that have exactly the same name as // the sourcCRS and targetCRS. Typically for the case of init=IGNF:XXXX // Transformation from IGNF:NTFP to IGNF:RGF93G, // using // NTF geographiques Paris (gr) vers NTF GEOGRAPHIQUES GREENWICH (DMS) + // NOUVELLE TRIANGULATION DE LA FRANCE (NTF) vers RGF93 (ETRS89) // that is using ntf_r93.gsb, is horribly dependent // of IGNF:RGF93G being returned before IGNF:RGF93GEO in candidatesDstGeod. // If RGF93GEO is returned before then we go through WGS84 and use // instead a Helmert transformation. // The below logic is thus quite fragile, and attempts at changing it // result in degraded results for other use cases... for (const auto &candidateSrcGeod : candidatesSrcGeod) { if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) { for (const auto &candidateDstGeod : candidatesDstGeod) { if (candidateDstGeod->nameStr() == targetCRS->nameStr()) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + "->" + objectAsStr(candidateSrcGeod.get()) + "->" + objectAsStr(candidateDstGeod.get()) + "->" + objectAsStr(targetCRS.get()) + ")"); #endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); assert(!opsFirst.empty()); const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); createTransformations(candidateSrcGeod, candidateDstGeod, opsFirst[0], isNullFirst); if (!res.empty()) { if (hasResultSetOnlyResultsWithPROJStep(res)) { continue; } return; } } } } } for (const auto &candidateSrcGeod : candidatesSrcGeod) { const bool bSameSrcName = candidateSrcGeod->nameStr() == sourceCRS->nameStr(); #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK(""); #endif const auto opsFirst = createOperations(sourceCRS, candidateSrcGeod, context); assert(!opsFirst.empty()); const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); for (const auto &candidateDstGeod : candidatesDstGeod) { if (bSameSrcName && candidateDstGeod->nameStr() == targetCRS->nameStr()) { continue; } #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + "->" + objectAsStr(candidateSrcGeod.get()) + "->" + objectAsStr(candidateDstGeod.get()) + "->" + objectAsStr(targetCRS.get()) + ")"); #endif createTransformations(candidateSrcGeod, candidateDstGeod, opsFirst[0], isNullFirst); if (!res.empty() && !hasResultSetOnlyResultsWithPROJStep(res)) { return; } } } } // --------------------------------------------------------------------------- static CoordinateOperationNNPtr createBallparkGeocentricTranslation(const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { std::string name(BALLPARK_GEOCENTRIC_TRANSLATION); name += " from "; name += sourceCRS->nameStr(); name += " to "; name += targetCRS->nameStr(); return util::nn_static_pointer_cast( Transformation::createGeocentricTranslations( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), sourceCRS, targetCRS, 0.0, 0.0, 0.0, {})); } // --------------------------------------------------------------------------- bool CoordinateOperationFactory::Private::hasPerfectAccuracyResult( const std::vector &res, const Context &context) { auto resTmp = FilterResults(res, context.context, context.extent1, context.extent2, true) .getRes(); for (const auto &op : resTmp) { const double acc = getAccuracy(op); if (acc == 0.0) { return true; } } return false; } // --------------------------------------------------------------------------- std::vector CoordinateOperationFactory::Private::createOperations( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("createOperations(" + objectAsStr(sourceCRS.get()) + " --> " + objectAsStr(targetCRS.get()) + ")"); #endif std::vector res; auto boundSrc = dynamic_cast(sourceCRS.get()); auto boundDst = dynamic_cast(targetCRS.get()); const auto &sourceProj4Ext = boundSrc ? boundSrc->baseCRS()->getExtensionProj4() : sourceCRS->getExtensionProj4(); const auto &targetProj4Ext = boundDst ? boundDst->baseCRS()->getExtensionProj4() : targetCRS->getExtensionProj4(); if (!sourceProj4Ext.empty() || !targetProj4Ext.empty()) { createOperationsFromProj4Ext(sourceCRS, targetCRS, boundSrc, boundDst, res); return res; } auto geodSrc = dynamic_cast(sourceCRS.get()); auto geodDst = dynamic_cast(targetCRS.get()); auto geogSrc = dynamic_cast(sourceCRS.get()); auto geogDst = dynamic_cast(targetCRS.get()); auto vertSrc = dynamic_cast(sourceCRS.get()); auto vertDst = dynamic_cast(targetCRS.get()); // First look-up if the registry provide us with operations. auto derivedSrc = dynamic_cast(sourceCRS.get()); auto derivedDst = dynamic_cast(targetCRS.get()); const auto &authFactory = context.context->getAuthorityFactory(); if (authFactory && (derivedSrc == nullptr || !derivedSrc->baseCRS()->_isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) && (derivedDst == nullptr || !derivedDst->baseCRS()->_isEquivalentTo( sourceCRS.get(), util::IComparable::Criterion::EQUIVALENT))) { if (createOperationsFromDatabase(sourceCRS, targetCRS, context, geodSrc, geodDst, geogSrc, geogDst, vertSrc, vertDst, res)) { return res; } } // Special case if both CRS are geodetic if (geodSrc && geodDst && !derivedSrc && !derivedDst) { createOperationsGeodToGeod(sourceCRS, targetCRS, context, geodSrc, geodDst, res); return res; } // If the source is a derived CRS, then chain the inverse of its // deriving conversion, with transforms from its baseCRS to the // targetCRS if (derivedSrc) { createOperationsDerivedTo(sourceCRS, targetCRS, context, derivedSrc, res); return res; } // reverse of previous case if (derivedDst) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } if (boundSrc && geogDst) { createOperationsBoundToGeog(sourceCRS, targetCRS, context, boundSrc, geogDst, res); return res; } // reverse of previous case if (geogSrc && boundDst) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } // vertCRS (as boundCRS with transformation to target vertCRS) to // vertCRS if (boundSrc && vertDst) { createOperationsBoundToVert(sourceCRS, targetCRS, context, boundSrc, vertDst, res); return res; } // reverse of previous case if (boundDst && vertSrc) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } if (vertSrc && vertDst) { createOperationsVertToVert(sourceCRS, targetCRS, context, vertSrc, vertDst, res); return res; } // A bit odd case as we are comparing apples to oranges, but in case // the vertical unit differ, do something useful. if (vertSrc && geogDst) { createOperationsVertToGeog(sourceCRS, targetCRS, context, vertSrc, geogDst, res); return res; } // reverse of previous case if (vertDst && geogSrc) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } // boundCRS to boundCRS using the same geographic hubCRS if (boundSrc && boundDst) { createOperationsBoundToBound(sourceCRS, targetCRS, context, boundSrc, boundDst, res); return res; } auto compoundSrc = dynamic_cast(sourceCRS.get()); // Order of comparison between the geogDst vs geodDst is impotant if (compoundSrc && geogDst) { createOperationsCompoundToGeog(sourceCRS, targetCRS, context, compoundSrc, geogDst, res); return res; } else if (compoundSrc && geodDst) { createOperationsCompoundToGeod(sourceCRS, targetCRS, context, compoundSrc, geodDst, res); return res; } // reverse of previous cases auto compoundDst = dynamic_cast(targetCRS.get()); if (geodSrc && compoundDst) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } if (compoundSrc && compoundDst) { createOperationsCompoundToCompound(sourceCRS, targetCRS, context, compoundSrc, compoundDst, res); return res; } // '+proj=longlat +ellps=GRS67 +nadgrids=@foo.gsb +type=crs' to // '+proj=longlat +ellps=GRS80 +nadgrids=@bar.gsb +geoidgrids=@bar.gtx // +type=crs' if (boundSrc && compoundDst) { createOperationsBoundToCompound(sourceCRS, targetCRS, context, boundSrc, compoundDst, res); return res; } // reverse of previous case if (boundDst && compoundSrc) { return applyInverse(createOperations(targetCRS, sourceCRS, context)); } return res; } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsFromProj4Ext( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector &res) { ENTER_FUNCTION(); auto sourceProjExportable = dynamic_cast( boundSrc ? boundSrc : sourceCRS.get()); auto targetProjExportable = dynamic_cast( boundDst ? boundDst : targetCRS.get()); if (!sourceProjExportable) { throw InvalidOperation("Source CRS is not PROJ exportable"); } if (!targetProjExportable) { throw InvalidOperation("Target CRS is not PROJ exportable"); } auto projFormatter = io::PROJStringFormatter::create(); projFormatter->setCRSExport(true); projFormatter->setLegacyCRSToCRSContext(true); projFormatter->startInversion(); sourceProjExportable->_exportToPROJString(projFormatter.get()); auto geogSrc = dynamic_cast(sourceCRS.get()); if (geogSrc) { auto tmpFormatter = io::PROJStringFormatter::create(); geogSrc->addAngularUnitConvertAndAxisSwap(tmpFormatter.get()); projFormatter->ingestPROJString(tmpFormatter->toString()); } projFormatter->stopInversion(); targetProjExportable->_exportToPROJString(projFormatter.get()); auto geogDst = dynamic_cast(targetCRS.get()); if (geogDst) { auto tmpFormatter = io::PROJStringFormatter::create(); geogDst->addAngularUnitConvertAndAxisSwap(tmpFormatter.get()); projFormatter->ingestPROJString(tmpFormatter->toString()); } const auto PROJString = projFormatter->toString(); auto properties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr())); res.emplace_back(SingleOperation::createPROJBased( properties, PROJString, sourceCRS, targetCRS, {})); } // --------------------------------------------------------------------------- bool CoordinateOperationFactory::Private::createOperationsFromDatabase( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res) { ENTER_FUNCTION(); if (geogSrc && vertDst) { res = createOperationsGeogToVertFromGeoid(sourceCRS, targetCRS, vertDst, context); } else if (geogDst && vertSrc) { res = applyInverse(createOperationsGeogToVertFromGeoid( targetCRS, sourceCRS, vertSrc, context)); } if (!res.empty()) { return true; } bool resFindDirectNonEmptyBeforeFiltering = false; res = findOpsInRegistryDirect(sourceCRS, targetCRS, context, resFindDirectNonEmptyBeforeFiltering); // If we get at least a result with perfect accuracy, do not // bother generating synthetic transforms. if (hasPerfectAccuracyResult(res, context)) { return true; } bool doFilterAndCheckPerfectOp = false; bool sameGeodeticDatum = false; if (vertSrc || vertDst) { if (res.empty()) { if (geogSrc && geogSrc->coordinateSystem()->axisList().size() == 2 && vertDst) { auto dbContext = context.context->getAuthorityFactory()->databaseContext(); auto resTmp = findOpsInRegistryDirect( sourceCRS->promoteTo3D(std::string(), dbContext), targetCRS, context, resFindDirectNonEmptyBeforeFiltering); for (auto &op : resTmp) { auto newOp = op->shallowClone(); setCRSs(newOp.get(), sourceCRS, targetCRS); res.emplace_back(newOp); } } else if (geogDst && geogDst->coordinateSystem()->axisList().size() == 2 && vertSrc) { auto dbContext = context.context->getAuthorityFactory()->databaseContext(); auto resTmp = findOpsInRegistryDirect( sourceCRS, targetCRS->promoteTo3D(std::string(), dbContext), context, resFindDirectNonEmptyBeforeFiltering); for (auto &op : resTmp) { auto newOp = op->shallowClone(); setCRSs(newOp.get(), sourceCRS, targetCRS); res.emplace_back(newOp); } } } if (res.empty()) { createOperationsFromDatabaseWithVertCRS(sourceCRS, targetCRS, context, geogSrc, geogDst, vertSrc, vertDst, res); } } else if (geodSrc && geodDst) { const auto &srcDatum = geodSrc->datum(); const auto &dstDatum = geodDst->datum(); sameGeodeticDatum = srcDatum != nullptr && dstDatum != nullptr && srcDatum->_isEquivalentTo(dstDatum.get(), util::IComparable::Criterion::EQUIVALENT); if (res.empty() && !sameGeodeticDatum && !context.inCreateOperationsWithDatumPivotAntiRecursion && srcDatum != nullptr && dstDatum != nullptr) { // If we still didn't find a transformation, and that the source // and target are GeodeticCRS, then go through their underlying // datum to find potential transformations between other // GeodeticCRSs // that are made of those datum // The typical example is if transforming between two // GeographicCRS, // but transformations are only available between their // corresponding geocentric CRS. createOperationsWithDatumPivot(res, sourceCRS, targetCRS, geodSrc, geodDst, context); doFilterAndCheckPerfectOp = !res.empty(); } } bool foundInstantiableOp = false; // FIXME: the limitation to .size() == 1 is just for the // -s EPSG:4959+5759 -t "EPSG:4959+7839" case // finding EPSG:7860 'NZVD2016 height to Auckland 1946 // height (1)', which uses the EPSG:1071 'Vertical Offset by Grid // Interpolation (NZLVD)' method which is not currently implemented by PROJ // (cannot deal with .csv files) // Initially the test was written to iterate over for all operations of a // non-empty res, but this causes failures in the test suite when no grids // are installed at all. Ideally we should tweak the test suite to be // robust to that, or skip some tests. if (res.size() == 1) { try { res.front()->exportToPROJString( io::PROJStringFormatter::create().get()); foundInstantiableOp = true; } catch (const std::exception &) { } if (!foundInstantiableOp) { resFindDirectNonEmptyBeforeFiltering = false; } } else if (res.size() > 1) { foundInstantiableOp = true; } // NAD27 to NAD83 has tens of results already. No need to look // for a pivot if (!sameGeodeticDatum && (((res.empty() || !foundInstantiableOp) && !resFindDirectNonEmptyBeforeFiltering && context.context->getAllowUseIntermediateCRS() == CoordinateOperationContext::IntermediateCRSUse:: IF_NO_DIRECT_TRANSFORMATION) || context.context->getAllowUseIntermediateCRS() == CoordinateOperationContext::IntermediateCRSUse::ALWAYS || getenv("PROJ_FORCE_SEARCH_PIVOT"))) { auto resWithIntermediate = findsOpsInRegistryWithIntermediate( sourceCRS, targetCRS, context, false); res.insert(res.end(), resWithIntermediate.begin(), resWithIntermediate.end()); doFilterAndCheckPerfectOp = !res.empty(); } else if (!context.inCreateOperationsWithDatumPivotAntiRecursion && !resFindDirectNonEmptyBeforeFiltering && geodSrc && geodDst && !sameGeodeticDatum && context.context->getIntermediateCRS().empty() && context.context->getAllowUseIntermediateCRS() != CoordinateOperationContext::IntermediateCRSUse::NEVER) { bool tryWithGeodeticDatumIntermediate = res.empty(); if (!tryWithGeodeticDatumIntermediate) { // This is in particular for the GDA94 to WGS 84 (G1762) case // As we have a WGS 84 -> WGS 84 (G1762) null-transformation in the // PROJ authority, previous steps might have use that WGS 84 // intermediate directly. They might also have generated a path // through ITRF2008, as there is a path // GDA94 (geoc.) -> ITRF2008 (geoc.) -> WGS84 (G1762) (geoc.) // But there's a better path using // GDA94 (geog.) --> GDA2020 (geog.) and // GDA2020 (geoc.) -> WGS84 (G1762) (geoc.) that requires to // explore intermediates through their datum, and not directly // trough the CRS code. // Do that only if the number of results we got through other // algorithms is small, or if all results we have go through an // operation in the PROJ authority. constexpr size_t ARBITRARY_SMALL_NUMBER = 5U; tryWithGeodeticDatumIntermediate = res.size() < ARBITRARY_SMALL_NUMBER || hasResultSetOnlyResultsWithPROJStep(res); } if (tryWithGeodeticDatumIntermediate) { auto resWithIntermediate = findsOpsInRegistryWithIntermediate( sourceCRS, targetCRS, context, true); res.insert(res.end(), resWithIntermediate.begin(), resWithIntermediate.end()); doFilterAndCheckPerfectOp = !res.empty(); } } if (doFilterAndCheckPerfectOp) { // If we get at least a result with perfect accuracy, do not bother // generating synthetic transforms. if (hasPerfectAccuracyResult(res, context)) { return true; } } return false; } // --------------------------------------------------------------------------- static std::vector findCandidateVertCRSForDatum(const io::AuthorityFactoryPtr &authFactory, const datum::VerticalReferenceFrame *datum) { std::vector candidates; assert(datum); const auto &ids = datum->identifiers(); const auto &datumName = datum->nameStr(); if (!ids.empty()) { for (const auto &id : ids) { const auto &authName = *(id->codeSpace()); const auto &code = id->code(); if (!authName.empty()) { auto l_candidates = authFactory->createVerticalCRSFromDatum(authName, code); for (const auto &candidate : l_candidates) { candidates.emplace_back(candidate); } } } } else if (datumName != "unknown" && datumName != "unnamed") { auto matches = authFactory->createObjectsFromName( datumName, {io::AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME}, false, 2); if (matches.size() == 1) { const auto &match = matches.front(); if (datum->_isEquivalentTo( match.get(), util::IComparable::Criterion::EQUIVALENT) && !match->identifiers().empty()) { return findCandidateVertCRSForDatum( authFactory, dynamic_cast( match.get())); } } } return candidates; } // --------------------------------------------------------------------------- std::vector CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Private::Context &context) { ENTER_FUNCTION(); const auto useTransf = [&targetCRS, &context, vertDst](const CoordinateOperationNNPtr &op) { const auto targetOp = dynamic_cast(op->targetCRS().get()); assert(targetOp); if (targetOp->_isEquivalentTo( vertDst, util::IComparable::Criterion::EQUIVALENT)) { return op; } std::vector tmp; createOperationsVertToVert(NN_NO_CHECK(op->targetCRS()), targetCRS, context, targetOp, vertDst, tmp); assert(!tmp.empty()); auto ret = ConcatenatedOperation::createComputeMetadata( {op, tmp.front()}, !allowEmptyIntersection); return ret; }; const auto getProjGeoidTransformation = [&sourceCRS, &targetCRS, &vertDst]( const CoordinateOperationNNPtr &model, const std::string &projFilename) { const auto getNameVertCRSMetre = [](const std::string &name) { if (name.empty()) return std::string("unnamed"); auto ret(name); bool haveOriginalUnit = false; if (name.back() == ')') { const auto pos = ret.rfind(" ("); if (pos != std::string::npos) { haveOriginalUnit = true; ret = ret.substr(0, pos); } } const auto pos = ret.rfind(" depth"); if (pos != std::string::npos) { ret = ret.substr(0, pos) + " height"; } if (!haveOriginalUnit) { ret += " (metre)"; } return ret; }; const auto &axis = vertDst->coordinateSystem()->axisList()[0]; const auto geogSrcCRS = dynamic_cast(model->interpolationCRS().get()) ? NN_NO_CHECK(model->interpolationCRS()) : sourceCRS; const auto vertCRSMetre = axis->unit() == common::UnitOfMeasure::METRE && axis->direction() == cs::AxisDirection::UP ? targetCRS : util::nn_static_pointer_cast( crs::VerticalCRS::create( util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, getNameVertCRSMetre(targetCRS->nameStr())), vertDst->datum(), vertDst->datumEnsemble(), cs::VerticalCS::createGravityRelatedHeight( common::UnitOfMeasure::METRE))); const auto properties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildOpName("Transformation", vertCRSMetre, geogSrcCRS)); return Transformation::createGravityRelatedHeightToGeographic3D( properties, vertCRSMetre, geogSrcCRS, nullptr, projFilename, {}); }; std::vector res; const auto &authFactory = context.context->getAuthorityFactory(); if (authFactory) { const auto &models = vertDst->geoidModel(); for (const auto &model : models) { const auto &modelName = model->nameStr(); const auto transformations = starts_with(modelName, "PROJ ") ? std::vector< CoordinateOperationNNPtr>{getProjGeoidTransformation( model, modelName.substr(strlen("PROJ ")))} : authFactory->getTransformationsForGeoid( modelName, context.context->getUsePROJAlternativeGridNames()); for (const auto &transf : transformations) { if (dynamic_cast( transf->sourceCRS().get()) && dynamic_cast( transf->targetCRS().get())) { res.push_back(useTransf(transf)); } else if (dynamic_cast( transf->targetCRS().get()) && dynamic_cast( transf->sourceCRS().get())) { res.push_back(useTransf(transf->inverse())); } } } } return res; } // --------------------------------------------------------------------------- std::vector CoordinateOperationFactory::Private:: createOperationsGeogToVertWithIntermediateVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Private::Context &context) { ENTER_FUNCTION(); std::vector res; struct AntiRecursionGuard { Context &context; explicit AntiRecursionGuard(Context &contextIn) : context(contextIn) { assert(!context.inCreateOperationsGeogToVertWithIntermediateVert); context.inCreateOperationsGeogToVertWithIntermediateVert = true; } ~AntiRecursionGuard() { context.inCreateOperationsGeogToVertWithIntermediateVert = false; } }; AntiRecursionGuard guard(context); const auto &authFactory = context.context->getAuthorityFactory(); auto candidatesVert = findCandidateVertCRSForDatum(authFactory, vertDst->datum().get()); for (const auto &candidateVert : candidatesVert) { auto resTmp = createOperations(sourceCRS, candidateVert, context); if (!resTmp.empty()) { const auto opsSecond = createOperations(candidateVert, targetCRS, context); if (!opsSecond.empty()) { // The transformation from candidateVert to targetCRS should // be just a unit change typically, so take only the first one, // which is likely/hopefully the only one. for (const auto &opFirst : resTmp) { if (hasIdentifiers(opFirst)) { if (candidateVert->_isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { res.emplace_back(opFirst); } else { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, opsSecond.front()}, !allowEmptyIntersection)); } } } if (!res.empty()) break; } } } return res; } // --------------------------------------------------------------------------- std::vector CoordinateOperationFactory::Private:: createOperationsGeogToVertWithAlternativeGeog( const crs::CRSNNPtr & /*sourceCRS*/, // geographic CRS const crs::CRSNNPtr &targetCRS, // vertical CRS Private::Context &context) { ENTER_FUNCTION(); std::vector res; struct AntiRecursionGuard { Context &context; explicit AntiRecursionGuard(Context &contextIn) : context(contextIn) { assert(!context.inCreateOperationsGeogToVertWithAlternativeGeog); context.inCreateOperationsGeogToVertWithAlternativeGeog = true; } ~AntiRecursionGuard() { context.inCreateOperationsGeogToVertWithAlternativeGeog = false; } }; AntiRecursionGuard guard(context); // Generally EPSG has operations from GeogCrs to VertCRS auto ops = findOpsInRegistryDirectTo(targetCRS, context); for (const auto &op : ops) { const auto tmpCRS = op->sourceCRS(); if (tmpCRS && dynamic_cast(tmpCRS.get())) { res.emplace_back(op); } } return res; } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private:: createOperationsFromDatabaseWithVertCRS( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res) { // Typically to transform from "NAVD88 height (ftUS)" to a geog CRS // by using transformations of "NAVD88 height" (metre) to that geog CRS if (res.empty() && !context.inCreateOperationsGeogToVertWithIntermediateVert && geogSrc && vertDst) { res = createOperationsGeogToVertWithIntermediateVert( sourceCRS, targetCRS, vertDst, context); } else if (res.empty() && !context.inCreateOperationsGeogToVertWithIntermediateVert && geogDst && vertSrc) { res = applyInverse(createOperationsGeogToVertWithIntermediateVert( targetCRS, sourceCRS, vertSrc, context)); } // NAD83 only exists in 2D version in EPSG, so if it has been // promoted to 3D, when researching a vertical to geog // transformation, try to down cast to 2D. const auto geog3DToVertTryThroughGeog2D = [&res, &context]( const crs::GeographicCRS *geogSrcIn, const crs::VerticalCRS *vertDstIn, const crs::CRSNNPtr &targetCRSIn) { if (res.empty() && geogSrcIn && vertDstIn && geogSrcIn->coordinateSystem()->axisList().size() == 3 && geogSrcIn->datum()) { const auto &authFactory = context.context->getAuthorityFactory(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( authFactory, geogSrcIn, geogSrcIn->datum().get())); for (const auto &candidate : candidatesSrcGeod) { auto geogCandidate = util::nn_dynamic_pointer_cast( candidate); if (geogCandidate && geogCandidate->coordinateSystem()->axisList().size() == 2) { bool ignored; res = findOpsInRegistryDirect(NN_NO_CHECK(geogCandidate), targetCRSIn, context, ignored); break; } } return true; } return false; }; if (geog3DToVertTryThroughGeog2D(geogSrc, vertDst, targetCRS)) { // do nothing } else if (geog3DToVertTryThroughGeog2D(geogDst, vertSrc, sourceCRS)) { res = applyInverse(res); } // There's no direct transformation from NAVD88 height to WGS84, // so try to research all transformations from NAVD88 to another // intermediate GeographicCRS. if (res.empty() && !context.inCreateOperationsGeogToVertWithAlternativeGeog && geogSrc && vertDst) { res = createOperationsGeogToVertWithAlternativeGeog(sourceCRS, targetCRS, context); } else if (res.empty() && !context.inCreateOperationsGeogToVertWithAlternativeGeog && geogDst && vertSrc) { res = applyInverse(createOperationsGeogToVertWithAlternativeGeog( targetCRS, sourceCRS, context)); } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsGeodToGeod( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, std::vector &res) { ENTER_FUNCTION(); if (geodSrc->ellipsoid()->celestialBody() != geodDst->ellipsoid()->celestialBody()) { throw util::UnsupportedOperationException( "Source and target ellipsoid do not belong to the same " "celestial body"); } auto geogSrc = dynamic_cast(geodSrc); auto geogDst = dynamic_cast(geodDst); if (geogSrc && geogDst) { createOperationsGeogToGeog(res, sourceCRS, targetCRS, geogSrc, geogDst); return; } const bool isSrcGeocentric = geodSrc->isGeocentric(); const bool isSrcGeographic = geogSrc != nullptr; const bool isTargetGeocentric = geodDst->isGeocentric(); const bool isTargetGeographic = geogDst != nullptr; if (((isSrcGeocentric && isTargetGeographic) || (isSrcGeographic && isTargetGeocentric)) && geodSrc->datum() != nullptr && geodDst->datum() != nullptr) { // Same datum ? if (geodSrc->datum()->_isEquivalentTo( geodDst->datum().get(), util::IComparable::Criterion::EQUIVALENT)) { res.emplace_back( Conversion::createGeographicGeocentric(sourceCRS, targetCRS)); } else if (isSrcGeocentric && geogDst) { std::string interm_crs_name(geogDst->nameStr()); interm_crs_name += " (geocentric)"; auto interm_crs = util::nn_static_pointer_cast(crs::GeodeticCRS::create( addDomains(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, interm_crs_name), geogDst), NN_NO_CHECK(geogDst->datum()), NN_CHECK_ASSERT( util::nn_dynamic_pointer_cast( geodSrc->coordinateSystem())))); auto opFirst = createBallparkGeocentricTranslation(sourceCRS, interm_crs); auto opSecond = Conversion::createGeographicGeocentric(interm_crs, targetCRS); res.emplace_back(ConcatenatedOperation::createComputeMetadata( {opFirst, opSecond}, !allowEmptyIntersection)); } else { // Apply previous case in reverse way std::vector resTmp; createOperationsGeodToGeod(targetCRS, sourceCRS, context, geodDst, geodSrc, resTmp); assert(resTmp.size() == 1); res.emplace_back(resTmp.front()->inverse()); } return; } if (isSrcGeocentric && isTargetGeocentric) { if (sourceCRS->_isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT) || (geodSrc->datum() != nullptr && geodDst->datum() != nullptr && geodSrc->datum()->_isEquivalentTo( geodDst->datum().get(), util::IComparable::Criterion::EQUIVALENT))) { std::string name(NULL_GEOCENTRIC_TRANSLATION); name += " from "; name += sourceCRS->nameStr(); name += " to "; name += targetCRS->nameStr(); res.emplace_back(Transformation::createGeocentricTranslations( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), sourceCRS, targetCRS, 0.0, 0.0, 0.0, {metadata::PositionalAccuracy::create("0")})); } else { res.emplace_back( createBallparkGeocentricTranslation(sourceCRS, targetCRS)); } return; } // Transformation between two geodetic systems of unknown type // This should normally not be triggered with "standard" CRS res.emplace_back(createGeodToGeodPROJBased(sourceCRS, targetCRS)); } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsDerivedTo( const crs::CRSNNPtr & /*sourceCRS*/, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::DerivedCRS *derivedSrc, std::vector &res) { ENTER_FUNCTION(); auto opFirst = derivedSrc->derivingConversion()->inverse(); // Small optimization if the targetCRS is the baseCRS of the source // derivedCRS. if (derivedSrc->baseCRS()->_isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { res.emplace_back(opFirst); return; } auto opsSecond = createOperations(derivedSrc->baseCRS(), targetCRS, context); for (const auto &opSecond : opsSecond) { try { res.emplace_back(ConcatenatedOperation::createComputeMetadata( {opFirst, opSecond}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsBoundToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::GeographicCRS *geogDst, std::vector &res) { ENTER_FUNCTION(); const auto &hubSrc = boundSrc->hubCRS(); auto hubSrcGeog = dynamic_cast(hubSrc.get()); auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS(); bool triedBoundCrsToGeogCRSSameAsHubCRS = false; // Is it: boundCRS to a geogCRS that is the same as the hubCRS ? if (hubSrcGeog && geogCRSOfBaseOfBoundSrc && (hubSrcGeog->_isEquivalentTo( geogDst, util::IComparable::Criterion::EQUIVALENT) || hubSrcGeog->is2DPartOf3D(NN_NO_CHECK(geogDst)))) { triedBoundCrsToGeogCRSSameAsHubCRS = true; if (boundSrc->baseCRS() == geogCRSOfBaseOfBoundSrc) { // Optimization to avoid creating a useless concatenated // operation res.emplace_back(boundSrc->transformation()); return; } auto opsFirst = createOperations( boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); if (!opsFirst.empty()) { for (const auto &opFirst : opsFirst) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, boundSrc->transformation()}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } if (!res.empty()) { return; } } // If the datum are equivalent, this is also fine } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() && geogDst->datum() && hubSrcGeog->datum()->_isEquivalentTo( geogDst->datum().get(), util::IComparable::Criterion::EQUIVALENT)) { auto opsFirst = createOperations( boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); auto opsLast = createOperations(hubSrc, targetCRS, context); if (!opsFirst.empty() && !opsLast.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, boundSrc->transformation(), opLast}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } } if (!res.empty()) { return; } } // Consider WGS 84 and NAD83 as equivalent in that context if the // geogCRSOfBaseOfBoundSrc ellipsoid is Clarke66 (for NAD27) // Case of "+proj=latlong +ellps=clrk66 // +nadgrids=ntv1_can.dat,conus" // to "+proj=latlong +datum=NAD83" } else if (geogCRSOfBaseOfBoundSrc && hubSrcGeog && hubSrcGeog->datum() && geogDst->datum() && geogCRSOfBaseOfBoundSrc->ellipsoid()->_isEquivalentTo( datum::Ellipsoid::CLARKE_1866.get(), util::IComparable::Criterion::EQUIVALENT) && hubSrcGeog->datum()->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6326.get(), util::IComparable::Criterion::EQUIVALENT) && geogDst->datum()->_isEquivalentTo( datum::GeodeticReferenceFrame::EPSG_6269.get(), util::IComparable::Criterion::EQUIVALENT)) { auto nnGeogCRSOfBaseOfBoundSrc = NN_NO_CHECK(geogCRSOfBaseOfBoundSrc); if (boundSrc->baseCRS()->_isEquivalentTo( nnGeogCRSOfBaseOfBoundSrc.get(), util::IComparable::Criterion::EQUIVALENT)) { auto transf = boundSrc->transformation()->shallowClone(); transf->setProperties(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildTransfName(boundSrc->baseCRS()->nameStr(), targetCRS->nameStr()))); transf->setCRSs(boundSrc->baseCRS(), targetCRS, nullptr); res.emplace_back(transf); return; } else { auto opsFirst = createOperations( boundSrc->baseCRS(), nnGeogCRSOfBaseOfBoundSrc, context); auto transf = boundSrc->transformation()->shallowClone(); transf->setProperties(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildTransfName(nnGeogCRSOfBaseOfBoundSrc->nameStr(), targetCRS->nameStr()))); transf->setCRSs(nnGeogCRSOfBaseOfBoundSrc, targetCRS, nullptr); if (!opsFirst.empty()) { for (const auto &opFirst : opsFirst) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, transf}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } if (!res.empty()) { return; } } } } if (hubSrcGeog && hubSrcGeog->_isEquivalentTo(geogDst, util::IComparable::Criterion::EQUIVALENT) && dynamic_cast(boundSrc->baseCRS().get())) { auto transfSrc = boundSrc->transformation()->sourceCRS(); if (dynamic_cast(transfSrc.get()) && !boundSrc->baseCRS()->_isEquivalentTo( transfSrc.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opsFirst = createOperations(boundSrc->baseCRS(), transfSrc, context); for (const auto &opFirst : opsFirst) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, boundSrc->transformation()}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } return; } res.emplace_back(boundSrc->transformation()); return; } if (!triedBoundCrsToGeogCRSSameAsHubCRS && hubSrcGeog && geogCRSOfBaseOfBoundSrc) { // This one should go to the above 'Is it: boundCRS to a geogCRS // that is the same as the hubCRS ?' case auto opsFirst = createOperations(sourceCRS, hubSrc, context); auto opsLast = createOperations(hubSrc, targetCRS, context); if (!opsFirst.empty() && !opsLast.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { // Exclude artificial transformations from the hub // to the target CRS if (!opLast->hasBallparkTransformation()) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, opLast}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } } } if (!res.empty()) { return; } } } auto vertCRSOfBaseOfBoundSrc = dynamic_cast(boundSrc->baseCRS().get()); if (vertCRSOfBaseOfBoundSrc && hubSrcGeog) { auto opsFirst = createOperations(sourceCRS, hubSrc, context); if (context.skipHorizontalTransformation) { if (!opsFirst.empty()) res = opsFirst; return; } else { auto opsSecond = createOperations(hubSrc, targetCRS, context); if (!opsFirst.empty() && !opsSecond.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsSecond) { // Exclude artificial transformations from the hub // to the target CRS if (!opLast->hasBallparkTransformation()) { try { res.emplace_back( ConcatenatedOperation:: createComputeMetadata( {opFirst, opLast}, !allowEmptyIntersection)); } catch ( const InvalidOperationEmptyIntersection &) { } } } } if (!res.empty()) { return; } } } } res = createOperations(boundSrc->baseCRS(), targetCRS, context); } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsBoundToVert( const crs::CRSNNPtr & /*sourceCRS*/, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::VerticalCRS *vertDst, std::vector &res) { ENTER_FUNCTION(); auto baseSrcVert = dynamic_cast(boundSrc->baseCRS().get()); const auto &hubSrc = boundSrc->hubCRS(); auto hubSrcVert = dynamic_cast(hubSrc.get()); if (baseSrcVert && hubSrcVert && vertDst->_isEquivalentTo(hubSrcVert, util::IComparable::Criterion::EQUIVALENT)) { res.emplace_back(boundSrc->transformation()); return; } res = createOperations(boundSrc->baseCRS(), targetCRS, context); } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsVertToVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context & /*context*/, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, std::vector &res) { ENTER_FUNCTION(); const auto &srcDatum = vertSrc->datum(); const auto &dstDatum = vertDst->datum(); const bool equivalentVDatum = (srcDatum && dstDatum && srcDatum->_isEquivalentTo(dstDatum.get(), util::IComparable::Criterion::EQUIVALENT)); const auto &srcAxis = vertSrc->coordinateSystem()->axisList()[0]; const double convSrc = srcAxis->unit().conversionToSI(); const auto &dstAxis = vertDst->coordinateSystem()->axisList()[0]; const double convDst = dstAxis->unit().conversionToSI(); const bool srcIsUp = srcAxis->direction() == cs::AxisDirection::UP; const bool srcIsDown = srcAxis->direction() == cs::AxisDirection::DOWN; const bool dstIsUp = dstAxis->direction() == cs::AxisDirection::UP; const bool dstIsDown = dstAxis->direction() == cs::AxisDirection::DOWN; const bool heightDepthReversal = ((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp)); const double factor = convSrc / convDst; auto name = buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr()); if (!equivalentVDatum) { name += BALLPARK_VERTICAL_TRANSFORMATION; auto conv = Transformation::createChangeVerticalUnit( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name), sourceCRS, targetCRS, // In case of a height depth reversal, we should probably have // 2 steps instead of putting a negative factor... common::Scale(heightDepthReversal ? -factor : factor), {}); conv->setHasBallparkTransformation(true); res.push_back(conv); } else if (convSrc != convDst || !heightDepthReversal) { auto conv = Conversion::createChangeVerticalUnit( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name), // In case of a height depth reversal, we should probably have // 2 steps instead of putting a negative factor... common::Scale(heightDepthReversal ? -factor : factor)); conv->setCRSs(sourceCRS, targetCRS, nullptr); res.push_back(conv); } else { auto conv = Conversion::createHeightDepthReversal( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name)); conv->setCRSs(sourceCRS, targetCRS, nullptr); res.push_back(conv); } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsVertToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::VerticalCRS *vertSrc, const crs::GeographicCRS *geogDst, std::vector &res) { ENTER_FUNCTION(); if (vertSrc->identifiers().empty()) { const auto &vertSrcName = vertSrc->nameStr(); const auto &authFactory = context.context->getAuthorityFactory(); if (authFactory != nullptr && vertSrcName != "unnamed" && vertSrcName != "unknown") { auto matches = authFactory->createObjectsFromName( vertSrcName, {io::AuthorityFactory::ObjectType::VERTICAL_CRS}, false, 2); if (matches.size() == 1) { const auto &match = matches.front(); if (vertSrc->_isEquivalentTo( match.get(), util::IComparable::Criterion::EQUIVALENT) && !match->identifiers().empty()) { res = createOperations( NN_NO_CHECK( util::nn_dynamic_pointer_cast( match)), targetCRS, context); return; } } } } const auto &srcAxis = vertSrc->coordinateSystem()->axisList()[0]; const double convSrc = srcAxis->unit().conversionToSI(); double convDst = 1.0; const auto &geogAxis = geogDst->coordinateSystem()->axisList(); bool dstIsUp = true; bool dstIsDown = true; if (geogAxis.size() == 3) { const auto &dstAxis = geogAxis[2]; convDst = dstAxis->unit().conversionToSI(); dstIsUp = dstAxis->direction() == cs::AxisDirection::UP; dstIsDown = dstAxis->direction() == cs::AxisDirection::DOWN; } const bool srcIsUp = srcAxis->direction() == cs::AxisDirection::UP; const bool srcIsDown = srcAxis->direction() == cs::AxisDirection::DOWN; const bool heightDepthReversal = ((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp)); const double factor = convSrc / convDst; auto conv = Transformation::createChangeVerticalUnit( util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr()) + BALLPARK_VERTICAL_TRANSFORMATION_NO_ELLIPSOID_VERT_HEIGHT), sourceCRS, targetCRS, common::Scale(heightDepthReversal ? -factor : factor), {}); conv->setHasBallparkTransformation(true); res.push_back(conv); } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsBoundToBound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::BoundCRS *boundDst, std::vector &res) { ENTER_FUNCTION(); const auto &hubSrc = boundSrc->hubCRS(); auto hubSrcGeog = dynamic_cast(hubSrc.get()); const auto &hubDst = boundDst->hubCRS(); auto hubDstGeog = dynamic_cast(hubDst.get()); auto geogCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractGeographicCRS(); auto geogCRSOfBaseOfBoundDst = boundDst->baseCRS()->extractGeographicCRS(); if (hubSrcGeog && hubDstGeog && hubSrcGeog->_isEquivalentTo(hubDstGeog, util::IComparable::Criterion::EQUIVALENT) && geogCRSOfBaseOfBoundSrc && geogCRSOfBaseOfBoundDst) { const bool firstIsNoOp = geogCRSOfBaseOfBoundSrc->_isEquivalentTo( boundSrc->baseCRS().get(), util::IComparable::Criterion::EQUIVALENT); const bool lastIsNoOp = geogCRSOfBaseOfBoundDst->_isEquivalentTo( boundDst->baseCRS().get(), util::IComparable::Criterion::EQUIVALENT); auto opsFirst = createOperations( boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); auto opsLast = createOperations(NN_NO_CHECK(geogCRSOfBaseOfBoundDst), boundDst->baseCRS(), context); if (!opsFirst.empty() && !opsLast.empty()) { const auto &opSecond = boundSrc->transformation(); auto opThird = boundDst->transformation()->inverse(); for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { try { std::vector ops; if (!firstIsNoOp) { ops.push_back(opFirst); } ops.push_back(opSecond); ops.push_back(opThird); if (!lastIsNoOp) { ops.push_back(opLast); } res.emplace_back( ConcatenatedOperation::createComputeMetadata( ops, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } } if (!res.empty()) { return; } } } auto vertCRSOfBaseOfBoundSrc = boundSrc->baseCRS()->extractVerticalCRS(); auto vertCRSOfBaseOfBoundDst = boundDst->baseCRS()->extractVerticalCRS(); if (hubSrcGeog && hubDstGeog && hubSrcGeog->_isEquivalentTo(hubDstGeog, util::IComparable::Criterion::EQUIVALENT) && vertCRSOfBaseOfBoundSrc && vertCRSOfBaseOfBoundDst) { auto opsFirst = createOperations(sourceCRS, hubSrc, context); auto opsLast = createOperations(hubSrc, targetCRS, context); if (!opsFirst.empty() && !opsLast.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opFirst, opLast}, !allowEmptyIntersection)); } catch (const InvalidOperationEmptyIntersection &) { } } } if (!res.empty()) { return; } } } res = createOperations(boundSrc->baseCRS(), boundDst->baseCRS(), context); } // --------------------------------------------------------------------------- static std::vector getOps(const CoordinateOperationNNPtr &op) { auto concatenated = dynamic_cast(op.get()); if (concatenated) return concatenated->operations(); return {op}; } // --------------------------------------------------------------------------- static bool useDifferentTransformationsForSameSourceTarget( const CoordinateOperationNNPtr &opA, const CoordinateOperationNNPtr &opB) { auto subOpsA = getOps(opA); auto subOpsB = getOps(opB); for (const auto &subOpA : subOpsA) { if (!dynamic_cast(subOpA.get())) continue; if (subOpA->sourceCRS()->nameStr() == "unknown" || subOpA->targetCRS()->nameStr() == "unknown") continue; for (const auto &subOpB : subOpsB) { if (!dynamic_cast(subOpB.get())) continue; if (subOpB->sourceCRS()->nameStr() == "unknown" || subOpB->targetCRS()->nameStr() == "unknown") continue; if (subOpA->sourceCRS()->nameStr() == subOpB->sourceCRS()->nameStr() && subOpA->targetCRS()->nameStr() == subOpB->targetCRS()->nameStr()) { if (starts_with(subOpA->nameStr(), NULL_GEOGRAPHIC_OFFSET) && starts_with(subOpB->nameStr(), NULL_GEOGRAPHIC_OFFSET)) { continue; } if (!subOpA->isEquivalentTo(subOpB.get())) { return true; } } else if (subOpA->sourceCRS()->nameStr() == subOpB->targetCRS()->nameStr() && subOpA->targetCRS()->nameStr() == subOpB->sourceCRS()->nameStr()) { if (starts_with(subOpA->nameStr(), NULL_GEOGRAPHIC_OFFSET) && starts_with(subOpB->nameStr(), NULL_GEOGRAPHIC_OFFSET)) { continue; } if (!subOpA->isEquivalentTo(subOpB->inverse().get())) { return true; } } } } return false; } // --------------------------------------------------------------------------- static crs::GeographicCRSPtr getInterpolationGeogCRS(const CoordinateOperationNNPtr &verticalTransform, const io::DatabaseContextPtr &dbContext) { crs::GeographicCRSPtr interpolationGeogCRS; auto transformationVerticalTransform = dynamic_cast(verticalTransform.get()); if (transformationVerticalTransform == nullptr) { const auto concat = dynamic_cast( verticalTransform.get()); if (concat) { const auto &steps = concat->operations(); // Is this change of unit and/or height depth reversal + // transformation ? for (const auto &step : steps) { const auto transf = dynamic_cast(step.get()); if (transf) { // Only support a single Transformation in the steps if (transformationVerticalTransform != nullptr) { transformationVerticalTransform = nullptr; break; } transformationVerticalTransform = transf; } } } } if (transformationVerticalTransform && !transformationVerticalTransform->hasBallparkTransformation()) { auto interpTransformCRS = transformationVerticalTransform->interpolationCRS(); if (interpTransformCRS) { interpolationGeogCRS = std::dynamic_pointer_cast( interpTransformCRS); } else { // If no explicit interpolation CRS, then // this will be the geographic CRS of the // vertical to geog transformation interpolationGeogCRS = std::dynamic_pointer_cast( transformationVerticalTransform->targetCRS().as_nullable()); } } if (interpolationGeogCRS) { if (interpolationGeogCRS->coordinateSystem()->axisList().size() == 3) { // We need to force the interpolation CRS, which // will // frequently be 3D, to 2D to avoid transformations // between source CRS and interpolation CRS to have // 3D terms. interpolationGeogCRS = interpolationGeogCRS->demoteTo2D(std::string(), dbContext) .as_nullable(); } } return interpolationGeogCRS; } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::GeographicCRS *geogDst, std::vector &res) { ENTER_FUNCTION(); const auto &authFactory = context.context->getAuthorityFactory(); const auto &componentsSrc = compoundSrc->componentReferenceSystems(); if (!componentsSrc.empty()) { if (componentsSrc.size() == 2) { auto derivedHSrc = dynamic_cast(componentsSrc[0].get()); if (derivedHSrc) { std::vector intermComponents{ derivedHSrc->baseCRS(), componentsSrc[1]}; auto properties = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, intermComponents[0]->nameStr() + " + " + intermComponents[1]->nameStr()); auto intermCompound = crs::CompoundCRS::create(properties, intermComponents); auto opsFirst = createOperations(sourceCRS, intermCompound, context); assert(!opsFirst.empty()); auto opsLast = createOperations(intermCompound, targetCRS, context); for (const auto &opLast : opsLast) { try { res.emplace_back( ConcatenatedOperation::createComputeMetadata( {opsFirst.front(), opLast}, !allowEmptyIntersection)); } catch (const std::exception &) { } } return; } } std::vector horizTransforms; auto srcGeogCRS = componentsSrc[0]->extractGeographicCRS(); if (srcGeogCRS) { horizTransforms = createOperations(componentsSrc[0], targetCRS, context); } std::vector verticalTransforms; const auto dbContext = authFactory ? authFactory->databaseContext().as_nullable() : nullptr; if (componentsSrc.size() >= 2 && componentsSrc[1]->extractVerticalCRS()) { struct SetSkipHorizontalTransform { Context &context; explicit SetSkipHorizontalTransform(Context &contextIn) : context(contextIn) { assert(!context.skipHorizontalTransformation); context.skipHorizontalTransformation = true; } ~SetSkipHorizontalTransform() { context.skipHorizontalTransformation = false; } }; SetSkipHorizontalTransform setSkipHorizontalTransform(context); verticalTransforms = createOperations( componentsSrc[1], targetCRS->promoteTo3D(std::string(), dbContext), context); bool foundRegisteredTransformWithAllGridsAvailable = false; const bool ignoreMissingGrids = context.context->getGridAvailabilityUse() == CoordinateOperationContext::GridAvailabilityUse:: IGNORE_GRID_AVAILABILITY; for (const auto &op : verticalTransforms) { if (hasIdentifiers(op) && dbContext) { bool missingGrid = false; if (!ignoreMissingGrids) { const auto gridsNeeded = op->gridsNeeded(dbContext); for (const auto &gridDesc : gridsNeeded) { if (!gridDesc.available) { missingGrid = true; break; } } } if (!missingGrid) { foundRegisteredTransformWithAllGridsAvailable = true; break; } } } if (!foundRegisteredTransformWithAllGridsAvailable && srcGeogCRS && !srcGeogCRS->_isEquivalentTo( geogDst, util::IComparable::Criterion::EQUIVALENT) && !srcGeogCRS->is2DPartOf3D(NN_NO_CHECK(geogDst))) { auto verticalTransformsTmp = createOperations( componentsSrc[1], NN_NO_CHECK(srcGeogCRS) ->promoteTo3D(std::string(), dbContext), context); bool foundRegisteredTransform = false; foundRegisteredTransformWithAllGridsAvailable = false; for (const auto &op : verticalTransformsTmp) { if (hasIdentifiers(op) && dbContext) { bool missingGrid = false; if (!ignoreMissingGrids) { const auto gridsNeeded = op->gridsNeeded(dbContext); for (const auto &gridDesc : gridsNeeded) { if (!gridDesc.available) { missingGrid = true; break; } } } foundRegisteredTransform = true; if (!missingGrid) { foundRegisteredTransformWithAllGridsAvailable = true; break; } } } if (foundRegisteredTransformWithAllGridsAvailable) { verticalTransforms = verticalTransformsTmp; } else if (foundRegisteredTransform) { verticalTransforms.insert(verticalTransforms.end(), verticalTransformsTmp.begin(), verticalTransformsTmp.end()); } } } if (horizTransforms.empty() || verticalTransforms.empty()) { res = horizTransforms; return; } typedef std::pair, std::vector> PairOfTransforms; std::map cacheHorizToInterpAndInterpToTarget; for (const auto &verticalTransform : verticalTransforms) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("Considering vertical transform " + objectAsStr(verticalTransform.get())); #endif crs::GeographicCRSPtr interpolationGeogCRS = getInterpolationGeogCRS(verticalTransform, dbContext); if (interpolationGeogCRS) { #ifdef TRACE_CREATE_OPERATIONS logTrace("Using " + objectAsStr(interpolationGeogCRS.get()) + " as interpolation CRS"); #endif std::vector srcToInterpOps; std::vector interpToTargetOps; std::string key; const auto &ids = interpolationGeogCRS->identifiers(); if (!ids.empty()) { key = (*ids.front()->codeSpace()) + ':' + ids.front()->code(); } const auto computeOpsToInterp = [&srcToInterpOps, &interpToTargetOps, &componentsSrc, &interpolationGeogCRS, &targetCRS, &dbContext, &context]() { srcToInterpOps = createOperations( componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS), context); auto target2D = targetCRS->demoteTo2D(std::string(), dbContext); if (!componentsSrc[0]->isEquivalentTo( target2D.get(), util::IComparable::Criterion::EQUIVALENT)) { interpToTargetOps = createOperations( NN_NO_CHECK(interpolationGeogCRS), targetCRS->demoteTo2D(std::string(), dbContext), context); } }; if (!key.empty()) { auto iter = cacheHorizToInterpAndInterpToTarget.find(key); if (iter == cacheHorizToInterpAndInterpToTarget.end()) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("looking for horizontal transformation " "from source to interpCRS and interpCRS to " "target"); #endif computeOpsToInterp(); cacheHorizToInterpAndInterpToTarget[key] = PairOfTransforms(srcToInterpOps, interpToTargetOps); } else { srcToInterpOps = iter->second.first; interpToTargetOps = iter->second.second; } } else { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("looking for horizontal transformation " "from source to interpCRS and interpCRS to " "target"); #endif computeOpsToInterp(); } #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("creating HorizVerticalHorizPROJBased operations"); #endif for (const auto &srcToInterp : srcToInterpOps) { if (interpToTargetOps.empty()) { try { auto op = createHorizVerticalHorizPROJBased( sourceCRS, targetCRS, srcToInterp, verticalTransform, srcToInterp->inverse(), interpolationGeogCRS, true); res.emplace_back(op); } catch (const std::exception &) { } } else { for (const auto &interpToTarget : interpToTargetOps) { if (useDifferentTransformationsForSameSourceTarget( srcToInterp, interpToTarget)) { continue; } try { auto op = createHorizVerticalHorizPROJBased( sourceCRS, targetCRS, srcToInterp, verticalTransform, interpToTarget, interpolationGeogCRS, true); res.emplace_back(op); } catch (const std::exception &) { } } } } } else { // This case is probably only correct if // verticalTransform and horizTransform are independent // and in particular that verticalTransform does not // involve a grid, because of the rather arbitrary order // horizontal then vertical applied for (const auto &horizTransform : horizTransforms) { try { auto op = createHorizVerticalPROJBased( sourceCRS, targetCRS, horizTransform, verticalTransform); res.emplace_back(op); } catch (const std::exception &) { } } } } } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsCompoundToGeod( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS * /*compoundSrc*/, const crs::GeodeticCRS *geodDst, std::vector &res) { auto datum = geodDst->datum(); if (datum) { auto cs = cs::EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight( common::UnitOfMeasure::DEGREE, common::UnitOfMeasure::METRE); auto intermGeog3DCRS = util::nn_static_pointer_cast(crs::GeographicCRS::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, geodDst->nameStr()) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), NN_NO_CHECK(datum), cs)); auto sourceToGeog3DOps = createOperations(sourceCRS, intermGeog3DCRS, context); auto geog3DToTargetOps = createOperations(intermGeog3DCRS, targetCRS, context); if (!geog3DToTargetOps.empty()) { for (const auto &op : sourceToGeog3DOps) { res.emplace_back(ConcatenatedOperation::createComputeMetadata( {op, geog3DToTargetOps.front()}, !allowEmptyIntersection)); } } } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::CompoundCRS *compoundDst, std::vector &res) { const auto &componentsSrc = compoundSrc->componentReferenceSystems(); const auto &componentsDst = compoundDst->componentReferenceSystems(); if (!componentsSrc.empty() && componentsSrc.size() == componentsDst.size()) { if (componentsSrc[0]->extractGeographicCRS() && componentsDst[0]->extractGeographicCRS()) { std::vector verticalTransforms; if (componentsSrc.size() >= 2 && componentsSrc[1]->extractVerticalCRS() && componentsDst[1]->extractVerticalCRS()) { if (!componentsSrc[1]->_isEquivalentTo( componentsDst[1].get())) { verticalTransforms = createOperations( componentsSrc[1], componentsDst[1], context); } } for (const auto &verticalTransform : verticalTransforms) { auto interpolationGeogCRS = NN_NO_CHECK(componentsSrc[0]->extractGeographicCRS()); auto transformationVerticalTransform = dynamic_cast( verticalTransform.get()); if (transformationVerticalTransform) { auto interpTransformCRS = transformationVerticalTransform->interpolationCRS(); if (interpTransformCRS) { auto nn_interpTransformCRS = NN_NO_CHECK(interpTransformCRS); if (dynamic_cast( nn_interpTransformCRS.get())) { interpolationGeogCRS = NN_NO_CHECK( util::nn_dynamic_pointer_cast< crs::GeographicCRS>(nn_interpTransformCRS)); } } } else { auto compSrc0BoundCrs = dynamic_cast(componentsSrc[0].get()); auto compDst0BoundCrs = dynamic_cast(componentsDst[0].get()); if (compSrc0BoundCrs && compDst0BoundCrs && dynamic_cast( compSrc0BoundCrs->hubCRS().get()) && compSrc0BoundCrs->hubCRS()->_isEquivalentTo( compDst0BoundCrs->hubCRS().get())) { interpolationGeogCRS = NN_NO_CHECK( util::nn_dynamic_pointer_cast( compSrc0BoundCrs->hubCRS())); } } auto opSrcCRSToGeogCRS = createOperations( componentsSrc[0], interpolationGeogCRS, context); auto opGeogCRStoDstCRS = createOperations( interpolationGeogCRS, componentsDst[0], context); for (const auto &opSrc : opSrcCRSToGeogCRS) { for (const auto &opDst : opGeogCRStoDstCRS) { try { auto op = createHorizVerticalHorizPROJBased( sourceCRS, targetCRS, opSrc, verticalTransform, opDst, interpolationGeogCRS, true); res.emplace_back(op); } catch (const InvalidOperationEmptyIntersection &) { } catch (const io::FormattingException &) { } } } } if (verticalTransforms.empty()) { auto resTmp = createOperations(componentsSrc[0], componentsDst[0], context); for (const auto &op : resTmp) { auto opClone = op->shallowClone(); setCRSs(opClone.get(), sourceCRS, targetCRS); res.emplace_back(opClone); } } } } } // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsBoundToCompound( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::BoundCRS *boundSrc, const crs::CompoundCRS *compoundDst, std::vector &res) { const auto &componentsDst = compoundDst->componentReferenceSystems(); if (!componentsDst.empty()) { auto compDst0BoundCrs = dynamic_cast(componentsDst[0].get()); if (compDst0BoundCrs) { auto boundSrcHubAsGeogCRS = dynamic_cast(boundSrc->hubCRS().get()); auto compDst0BoundCrsHubAsGeogCRS = dynamic_cast( compDst0BoundCrs->hubCRS().get()); if (boundSrcHubAsGeogCRS && compDst0BoundCrsHubAsGeogCRS) { const auto &boundSrcHubAsGeogCRSDatum = boundSrcHubAsGeogCRS->datum(); const auto &compDst0BoundCrsHubAsGeogCRSDatum = compDst0BoundCrsHubAsGeogCRS->datum(); if (boundSrcHubAsGeogCRSDatum && compDst0BoundCrsHubAsGeogCRSDatum && boundSrcHubAsGeogCRSDatum->_isEquivalentTo( compDst0BoundCrsHubAsGeogCRSDatum.get())) { auto cs = cs::EllipsoidalCS:: createLatitudeLongitudeEllipsoidalHeight( common::UnitOfMeasure::DEGREE, common::UnitOfMeasure::METRE); auto intermGeog3DCRS = util::nn_static_pointer_cast< crs::CRS>(crs::GeographicCRS::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, boundSrcHubAsGeogCRS->nameStr()) .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), NN_NO_CHECK(boundSrcHubAsGeogCRSDatum), cs)); auto sourceToGeog3DOps = createOperations(sourceCRS, intermGeog3DCRS, context); auto geog3DToTargetOps = createOperations(intermGeog3DCRS, targetCRS, context); for (const auto &opSrc : sourceToGeog3DOps) { for (const auto &opDst : geog3DToTargetOps) { if (opSrc->targetCRS() && opDst->sourceCRS() && !opSrc->targetCRS()->_isEquivalentTo( opDst->sourceCRS().get())) { // Shouldn't happen normally, but typically // one of them can be 2D and the other 3D // due to above createOperations() not // exactly setting the expected source and // target CRS. // So create an adapter operation... auto intermOps = createOperations( NN_NO_CHECK(opSrc->targetCRS()), NN_NO_CHECK(opDst->sourceCRS()), context); if (!intermOps.empty()) { res.emplace_back( ConcatenatedOperation:: createComputeMetadata( {opSrc, intermOps.front(), opDst}, !allowEmptyIntersection)); } } else { res.emplace_back( ConcatenatedOperation:: createComputeMetadata( {opSrc, opDst}, !allowEmptyIntersection)); } } } } } } } } //! @endcond // --------------------------------------------------------------------------- static crs::CRSNNPtr getResolvedCRS(const crs::CRSNNPtr &crs, const CoordinateOperationContextNNPtr &context, metadata::ExtentPtr &extentOut) { const auto &authFactory = context->getAuthorityFactory(); const auto &ids = crs->identifiers(); const auto &name = crs->nameStr(); bool approxExtent; extentOut = getExtentPossiblySynthetized(crs, approxExtent); // We try to "identify" the provided CRS with the ones of the database, // but in a more restricted way that what identify() does. // If we get a match from id in priority, and from name as a fallback, and // that they are equivalent to the input CRS, then use the identified CRS. // Even if they aren't equivalent, we update extentOut with the one of the // identified CRS if our input one is absent/not reliable. const auto tryToIdentifyByName = [&crs, &name, &authFactory, approxExtent, &extentOut]( io::AuthorityFactory::ObjectType objectType) { if (name != "unknown" && name != "unnamed") { auto matches = authFactory->createObjectsFromName( name, {objectType}, false, 2); if (matches.size() == 1) { const auto match = util::nn_static_pointer_cast(matches.front()); if (approxExtent || !extentOut) { extentOut = getExtent(match); } if (match->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { return match; } } } return crs; }; auto geogCRS = dynamic_cast(crs.get()); if (geogCRS && authFactory) { if (!ids.empty()) { const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), *ids.front()->codeSpace()); try { auto resolvedCrs( tmpAuthFactory->createGeographicCRS(ids.front()->code())); if (approxExtent || !extentOut) { extentOut = getExtent(resolvedCrs); } if (resolvedCrs->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { return util::nn_static_pointer_cast(resolvedCrs); } } catch (const std::exception &) { } } else { return tryToIdentifyByName( geogCRS->coordinateSystem()->axisList().size() == 2 ? io::AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS : io::AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); } } auto projectedCrs = dynamic_cast(crs.get()); if (projectedCrs && authFactory) { if (!ids.empty()) { const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), *ids.front()->codeSpace()); try { auto resolvedCrs( tmpAuthFactory->createProjectedCRS(ids.front()->code())); if (approxExtent || !extentOut) { extentOut = getExtent(resolvedCrs); } if (resolvedCrs->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { return util::nn_static_pointer_cast(resolvedCrs); } } catch (const std::exception &) { } } else { return tryToIdentifyByName( io::AuthorityFactory::ObjectType::PROJECTED_CRS); } } auto compoundCrs = dynamic_cast(crs.get()); if (compoundCrs && authFactory) { if (!ids.empty()) { const auto tmpAuthFactory = io::AuthorityFactory::create( authFactory->databaseContext(), *ids.front()->codeSpace()); try { auto resolvedCrs( tmpAuthFactory->createCompoundCRS(ids.front()->code())); if (approxExtent || !extentOut) { extentOut = getExtent(resolvedCrs); } if (resolvedCrs->isEquivalentTo( crs.get(), util::IComparable::Criterion::EQUIVALENT)) { return util::nn_static_pointer_cast(resolvedCrs); } } catch (const std::exception &) { } } else { auto outCrs = tryToIdentifyByName( io::AuthorityFactory::ObjectType::COMPOUND_CRS); const auto &components = compoundCrs->componentReferenceSystems(); if (outCrs.get() != crs.get()) { bool hasGeoid = false; if (components.size() == 2) { auto vertCRS = dynamic_cast(components[1].get()); if (vertCRS && !vertCRS->geoidModel().empty()) { hasGeoid = true; } } if (!hasGeoid) { return outCrs; } } if (approxExtent || !extentOut) { // If we still did not get a reliable extent, then try to // resolve the components of the compoundCRS, and take the // intersection of their extent. extentOut = metadata::ExtentPtr(); for (const auto &component : components) { metadata::ExtentPtr componentExtent; getResolvedCRS(component, context, componentExtent); if (extentOut && componentExtent) extentOut = extentOut->intersection( NN_NO_CHECK(componentExtent)); else if (componentExtent) extentOut = componentExtent; } } } } return crs; } // --------------------------------------------------------------------------- /** \brief Find a list of CoordinateOperation from sourceCRS to targetCRS. * * The operations are sorted with the most relevant ones first: by * descending * area (intersection of the transformation area with the area of interest, * or intersection of the transformation with the area of use of the CRS), * and * by increasing accuracy. Operations with unknown accuracy are sorted last, * whatever their area. * * When one of the source or target CRS has a vertical component but not the * other one, the one that has no vertical component is automatically promoted * to a 3D version, where its vertical axis is the ellipsoidal height in metres, * using the ellipsoid of the base geodetic CRS. * * @param sourceCRS source CRS. * @param targetCRS target CRS. * @param context Search context. * @return a list */ std::vector CoordinateOperationFactory::createOperations( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const CoordinateOperationContextNNPtr &context) const { #ifdef TRACE_CREATE_OPERATIONS ENTER_FUNCTION(); #endif // Look if we are called on CRS that have a link to a 'canonical' // BoundCRS // If so, use that one as input const auto &srcBoundCRS = sourceCRS->canonicalBoundCRS(); const auto &targetBoundCRS = targetCRS->canonicalBoundCRS(); auto l_sourceCRS = srcBoundCRS ? NN_NO_CHECK(srcBoundCRS) : sourceCRS; auto l_targetCRS = targetBoundCRS ? NN_NO_CHECK(targetBoundCRS) : targetCRS; metadata::ExtentPtr sourceCRSExtent; auto l_resolvedSourceCRS = getResolvedCRS(l_sourceCRS, context, sourceCRSExtent); metadata::ExtentPtr targetCRSExtent; auto l_resolvedTargetCRS = getResolvedCRS(l_targetCRS, context, targetCRSExtent); Private::Context contextPrivate(sourceCRSExtent, targetCRSExtent, context); if (context->getSourceAndTargetCRSExtentUse() == CoordinateOperationContext::SourceTargetCRSExtentUse::INTERSECTION) { if (sourceCRSExtent && targetCRSExtent && !sourceCRSExtent->intersects(NN_NO_CHECK(targetCRSExtent))) { return std::vector(); } } return filterAndSort(Private::createOperations(l_resolvedSourceCRS, l_resolvedTargetCRS, contextPrivate), context, sourceCRSExtent, targetCRSExtent); } // --------------------------------------------------------------------------- /** \brief Instantiate a CoordinateOperationFactory. */ CoordinateOperationFactoryNNPtr CoordinateOperationFactory::create() { return NN_NO_CHECK( CoordinateOperationFactory::make_unique()); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress InverseCoordinateOperation::~InverseCoordinateOperation() = default; // --------------------------------------------------------------------------- InverseCoordinateOperation::InverseCoordinateOperation( const CoordinateOperationNNPtr &forwardOperationIn, bool wktSupportsInversion) : forwardOperation_(forwardOperationIn), wktSupportsInversion_(wktSupportsInversion) {} // --------------------------------------------------------------------------- void InverseCoordinateOperation::setPropertiesFromForward() { setProperties( createPropertiesForInverse(forwardOperation_.get(), false, false)); setAccuracies(forwardOperation_->coordinateOperationAccuracies()); if (forwardOperation_->sourceCRS() && forwardOperation_->targetCRS()) { setCRSs(forwardOperation_.get(), true); } setHasBallparkTransformation( forwardOperation_->hasBallparkTransformation()); } // --------------------------------------------------------------------------- CoordinateOperationNNPtr InverseCoordinateOperation::inverse() const { return forwardOperation_; } // --------------------------------------------------------------------------- void InverseCoordinateOperation::_exportToPROJString( io::PROJStringFormatter *formatter) const { formatter->startInversion(); forwardOperation_->_exportToPROJString(formatter); formatter->stopInversion(); } // --------------------------------------------------------------------------- bool InverseCoordinateOperation::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherICO = dynamic_cast(other); if (otherICO == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) { return false; } return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion, dbContext); } // --------------------------------------------------------------------------- PROJBasedOperation::~PROJBasedOperation() = default; // --------------------------------------------------------------------------- PROJBasedOperation::PROJBasedOperation(const OperationMethodNNPtr &methodIn) : SingleOperation(methodIn) {} // --------------------------------------------------------------------------- PROJBasedOperationNNPtr PROJBasedOperation::create( const util::PropertyMap &properties, const std::string &PROJString, const crs::CRSPtr &sourceCRS, const crs::CRSPtr &targetCRS, const std::vector &accuracies) { auto method = OperationMethod::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, "PROJ-based operation method: " + PROJString), std::vector{}); auto op = PROJBasedOperation::nn_make_shared(method); op->assignSelf(op); op->projString_ = PROJString; if (sourceCRS && targetCRS) { op->setCRSs(NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), nullptr); } op->setProperties( addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation")); op->setAccuracies(accuracies); return op; } // --------------------------------------------------------------------------- PROJBasedOperationNNPtr PROJBasedOperation::create( const util::PropertyMap &properties, const io::IPROJStringExportableNNPtr &projExportable, bool inverse, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const std::vector &accuracies, bool hasBallparkTransformation) { auto formatter = io::PROJStringFormatter::create(); if (inverse) { formatter->startInversion(); } projExportable->_exportToPROJString(formatter.get()); if (inverse) { formatter->stopInversion(); } auto projString = formatter->toString(); auto method = OperationMethod::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, "PROJ-based operation method (approximate): " + projString), std::vector{}); auto op = PROJBasedOperation::nn_make_shared(method); op->assignSelf(op); op->projString_ = projString; op->setCRSs(sourceCRS, targetCRS, nullptr); op->setProperties( addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation")); op->setAccuracies(accuracies); op->projStringExportable_ = projExportable.as_nullable(); op->inverse_ = inverse; op->setHasBallparkTransformation(hasBallparkTransformation); return op; } // --------------------------------------------------------------------------- CoordinateOperationNNPtr PROJBasedOperation::inverse() const { if (projStringExportable_) { return util::nn_static_pointer_cast( PROJBasedOperation::create( createPropertiesForInverse(this, false, false), NN_NO_CHECK(projStringExportable_), !inverse_, NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()), coordinateOperationAccuracies(), hasBallparkTransformation())); } auto formatter = io::PROJStringFormatter::create(); formatter->startInversion(); try { formatter->ingestPROJString(projString_); } catch (const io::ParsingException &e) { throw util::UnsupportedOperationException( std::string("PROJBasedOperation::inverse() failed: ") + e.what()); } formatter->stopInversion(); auto op = PROJBasedOperation::create( createPropertiesForInverse(this, false, false), formatter->toString(), targetCRS(), sourceCRS(), coordinateOperationAccuracies()); op->setHasBallparkTransformation(hasBallparkTransformation()); return util::nn_static_pointer_cast(op); } // --------------------------------------------------------------------------- void PROJBasedOperation::_exportToWKT(io::WKTFormatter *formatter) const { if (sourceCRS() && targetCRS()) { exportTransformationToWKT(formatter); return; } const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2; if (!isWKT2) { throw io::FormattingException( "PROJBasedOperation can only be exported to WKT2"); } formatter->startNode(io::WKTConstants::CONVERSION, false); formatter->addQuotedString(nameStr()); method()->_exportToWKT(formatter); for (const auto ¶mValue : parameterValues()) { paramValue->_exportToWKT(formatter); } formatter->endNode(); } // --------------------------------------------------------------------------- void PROJBasedOperation::_exportToJSON( io::JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); auto objectContext(formatter->MakeObjectContext( (sourceCRS() && targetCRS()) ? "Transformation" : "Conversion", !identifiers().empty())); writer.AddObjKey("name"); auto l_name = nameStr(); if (l_name.empty()) { writer.Add("unnamed"); } else { writer.Add(l_name); } if (sourceCRS() && targetCRS()) { writer.AddObjKey("source_crs"); formatter->setAllowIDInImmediateChild(); sourceCRS()->_exportToJSON(formatter); writer.AddObjKey("target_crs"); formatter->setAllowIDInImmediateChild(); targetCRS()->_exportToJSON(formatter); } writer.AddObjKey("method"); formatter->setOmitTypeInImmediateChild(); formatter->setAllowIDInImmediateChild(); method()->_exportToJSON(formatter); const auto &l_parameterValues = parameterValues(); if (!l_parameterValues.empty()) { writer.AddObjKey("parameters"); { auto parametersContext(writer.MakeArrayContext(false)); for (const auto &genOpParamvalue : l_parameterValues) { formatter->setAllowIDInImmediateChild(); formatter->setOmitTypeInImmediateChild(); genOpParamvalue->_exportToJSON(formatter); } } } } // --------------------------------------------------------------------------- void PROJBasedOperation::_exportToPROJString( io::PROJStringFormatter *formatter) const { if (projStringExportable_) { if (inverse_) { formatter->startInversion(); } projStringExportable_->_exportToPROJString(formatter); if (inverse_) { formatter->stopInversion(); } return; } try { formatter->ingestPROJString(projString_); } catch (const io::ParsingException &e) { throw io::FormattingException( std::string("PROJBasedOperation::exportToPROJString() failed: ") + e.what()); } } // --------------------------------------------------------------------------- CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const { auto op = PROJBasedOperation::nn_make_shared(*this); op->assignSelf(op); op->setCRSs(this, false); return util::nn_static_pointer_cast(op); } // --------------------------------------------------------------------------- std::set PROJBasedOperation::gridsNeeded( const io::DatabaseContextPtr &databaseContext) const { std::set res; try { auto formatterOut = io::PROJStringFormatter::create(); auto formatter = io::PROJStringFormatter::create(); formatter->ingestPROJString(exportToPROJString(formatterOut.get())); const auto usedGridNames = formatter->getUsedGridNames(); for (const auto &shortName : usedGridNames) { GridDescription desc; desc.shortName = shortName; if (databaseContext) { databaseContext->lookForGridInfo( desc.shortName, desc.fullName, desc.packageName, desc.url, desc.directDownload, desc.openLicense, desc.available); } res.insert(desc); } } catch (const io::ParsingException &) { } return res; } //! @endcond // --------------------------------------------------------------------------- } // namespace operation NS_PROJ_END proj-6.3.1/src/iso19111/factory.cpp0000664000175000017500000075571013620030240013554 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/coordinateoperation_internal.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj/internal/lru_cache.hpp" #include "proj/internal/tracing.hpp" #include #include #include #include #include #include #include #include // std::ostringstream #include #include "proj_constants.h" // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_internal.h" #include "proj_api.h" // clang-format on #include // Custom SQLite VFS as our database is not supposed to be modified in // parallel. This is slightly faster #define ENABLE_CUSTOM_LOCKLESS_VFS using namespace NS_PROJ::internal; using namespace NS_PROJ::common; NS_PROJ_START namespace io { //! @cond Doxygen_Suppress // CRS subtypes #define GEOG_2D "geographic 2D" #define GEOG_3D "geographic 3D" #define GEOCENTRIC "geocentric" #define PROJECTED "projected" #define VERTICAL "vertical" #define COMPOUND "compound" #define GEOG_2D_SINGLE_QUOTED "'geographic 2D'" #define GEOG_3D_SINGLE_QUOTED "'geographic 3D'" #define GEOCENTRIC_SINGLE_QUOTED "'geocentric'" // --------------------------------------------------------------------------- struct SQLValues { enum class Type { STRING, DOUBLE }; // cppcheck-suppress noExplicitConstructor SQLValues(const std::string &value) : type_(Type::STRING), str_(value) {} // cppcheck-suppress noExplicitConstructor SQLValues(double value) : type_(Type::DOUBLE), double_(value) {} const Type &type() const { return type_; } // cppcheck-suppress functionStatic const std::string &stringValue() const { return str_; } // cppcheck-suppress functionStatic double doubleValue() const { return double_; } private: Type type_; std::string str_{}; double double_ = 0.0; }; // --------------------------------------------------------------------------- using SQLRow = std::vector; using SQLResultSet = std::list; using ListOfParams = std::list; // --------------------------------------------------------------------------- struct DatabaseContext::Private { Private(); ~Private(); void open(const std::string &databasePath, PJ_CONTEXT *ctx); void setHandle(sqlite3 *sqlite_handle); sqlite3 *handle() const { return sqlite_handle_; } PJ_CONTEXT *pjCtxt() const { return pjCtxt_; } void setPjCtxt(PJ_CONTEXT *ctxt) { pjCtxt_ = ctxt; } SQLResultSet run(const std::string &sql, const ListOfParams ¶meters = ListOfParams()); std::vector getDatabaseStructure(); // cppcheck-suppress functionStatic const std::string &getPath() const { return databasePath_; } void attachExtraDatabases( const std::vector &auxiliaryDatabasePaths); // Mechanism to detect recursion in calls from // AuthorityFactory::createXXX() -> createFromUserInput() -> // AuthorityFactory::createXXX() struct RecursionDetector { explicit RecursionDetector(const DatabaseContextNNPtr &context) : dbContext_(context) { if (dbContext_->getPrivate()->recLevel_ == 2) { // Throw exception before incrementing, since the destructor // will not be called throw FactoryException("Too many recursive calls"); } ++dbContext_->getPrivate()->recLevel_; } ~RecursionDetector() { --dbContext_->getPrivate()->recLevel_; } private: DatabaseContextNNPtr dbContext_; }; std::map> &getMapCanonicalizeGRFName() { return mapCanonicalizeGRFName_; } // cppcheck-suppress functionStatic common::UnitOfMeasurePtr getUOMFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const common::UnitOfMeasureNNPtr &uom); // cppcheck-suppress functionStatic crs::CRSPtr getCRSFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const crs::CRSNNPtr &crs); datum::GeodeticReferenceFramePtr // cppcheck-suppress functionStatic getGeodeticDatumFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const datum::GeodeticReferenceFrameNNPtr &datum); datum::EllipsoidPtr // cppcheck-suppress functionStatic getEllipsoidFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const datum::EllipsoidNNPtr &ellipsoid); datum::PrimeMeridianPtr // cppcheck-suppress functionStatic getPrimeMeridianFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const datum::PrimeMeridianNNPtr &pm); // cppcheck-suppress functionStatic cs::CoordinateSystemPtr getCoordinateSystemFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const cs::CoordinateSystemNNPtr &cs); // cppcheck-suppress functionStatic metadata::ExtentPtr getExtentFromCache(const std::string &code); // cppcheck-suppress functionStatic void cache(const std::string &code, const metadata::ExtentNNPtr &extent); // cppcheck-suppress functionStatic bool getCRSToCRSCoordOpFromCache( const std::string &code, std::vector &list); // cppcheck-suppress functionStatic void cache(const std::string &code, const std::vector &list); struct GridInfoCache { std::string fullFilename{}; std::string packageName{}; std::string url{}; bool found = false; bool directDownload = false; bool openLicense = false; bool gridAvailable = false; }; // cppcheck-suppress functionStatic bool getGridInfoFromCache(const std::string &code, GridInfoCache &info); // cppcheck-suppress functionStatic void cache(const std::string &code, const GridInfoCache &info); private: friend class DatabaseContext; std::string databasePath_{}; bool close_handle_ = true; sqlite3 *sqlite_handle_{}; std::map mapSqlToStatement_{}; PJ_CONTEXT *pjCtxt_ = nullptr; int recLevel_ = 0; bool detach_ = false; std::string lastMetadataValue_{}; std::map> mapCanonicalizeGRFName_{}; using LRUCacheOfObjects = lru11::Cache; static constexpr size_t CACHE_SIZE = 128; LRUCacheOfObjects cacheUOM_{CACHE_SIZE}; LRUCacheOfObjects cacheCRS_{CACHE_SIZE}; LRUCacheOfObjects cacheEllipsoid_{CACHE_SIZE}; LRUCacheOfObjects cacheGeodeticDatum_{CACHE_SIZE}; LRUCacheOfObjects cachePrimeMeridian_{CACHE_SIZE}; LRUCacheOfObjects cacheCS_{CACHE_SIZE}; LRUCacheOfObjects cacheExtent_{CACHE_SIZE}; lru11::Cache> cacheCRSToCrsCoordOp_{CACHE_SIZE}; lru11::Cache cacheGridInfo_{CACHE_SIZE}; std::map> cacheAllowedAuthorities_{}; lru11::Cache> cacheAliasNames_{ CACHE_SIZE}; static void insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj); static void getFromCache(LRUCacheOfObjects &cache, const std::string &code, util::BaseObjectPtr &obj); void closeDB() noexcept; // cppcheck-suppress functionStatic void registerFunctions(); #ifdef ENABLE_CUSTOM_LOCKLESS_VFS std::string thisNamePtr_{}; sqlite3_vfs *vfs_{}; bool createCustomVFS(); #endif Private(const Private &) = delete; Private &operator=(const Private &) = delete; }; // --------------------------------------------------------------------------- DatabaseContext::Private::Private() = default; // --------------------------------------------------------------------------- DatabaseContext::Private::~Private() { assert(recLevel_ == 0); closeDB(); #ifdef ENABLE_CUSTOM_LOCKLESS_VFS if (vfs_) { sqlite3_vfs_unregister(vfs_); delete vfs_; } #endif } // --------------------------------------------------------------------------- void DatabaseContext::Private::closeDB() noexcept { if (detach_) { // Workaround a bug visible in SQLite 3.8.1 and 3.8.2 that causes // a crash in TEST(factory, attachExtraDatabases_auxiliary) // due to possible wrong caching of key info. // The bug is specific to using a memory file with shared cache as an // auxiliary DB. // The efinitive fix was likely in 3.8.8 // https://github.com/mackyle/sqlite/commit/d412d4b8731991ecbd8811874aa463d0821673eb // But just after 3.8.2, // https://github.com/mackyle/sqlite/commit/ccf328c4318eacedab9ed08c404bc4f402dcad19 // also seemed to hide the issue. // Detaching a database hides the issue, not sure if it is by chance... try { run("DETACH DATABASE db_0"); } catch (...) { } detach_ = false; } for (auto &pair : mapSqlToStatement_) { sqlite3_finalize(pair.second); } mapSqlToStatement_.clear(); if (close_handle_ && sqlite_handle_ != nullptr) { sqlite3_close(sqlite_handle_); sqlite_handle_ = nullptr; } } // --------------------------------------------------------------------------- void DatabaseContext::Private::insertIntoCache(LRUCacheOfObjects &cache, const std::string &code, const util::BaseObjectPtr &obj) { cache.insert(code, obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::getFromCache(LRUCacheOfObjects &cache, const std::string &code, util::BaseObjectPtr &obj) { cache.tryGet(code, obj); } // --------------------------------------------------------------------------- bool DatabaseContext::Private::getCRSToCRSCoordOpFromCache( const std::string &code, std::vector &list) { return cacheCRSToCrsCoordOp_.tryGet(code, list); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache( const std::string &code, const std::vector &list) { cacheCRSToCrsCoordOp_.insert(code, list); } // --------------------------------------------------------------------------- crs::CRSPtr DatabaseContext::Private::getCRSFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheCRS_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const crs::CRSNNPtr &crs) { insertIntoCache(cacheCRS_, code, crs.as_nullable()); } // --------------------------------------------------------------------------- common::UnitOfMeasurePtr DatabaseContext::Private::getUOMFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheUOM_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const common::UnitOfMeasureNNPtr &uom) { insertIntoCache(cacheUOM_, code, uom.as_nullable()); } // --------------------------------------------------------------------------- datum::GeodeticReferenceFramePtr DatabaseContext::Private::getGeodeticDatumFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheGeodeticDatum_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache( const std::string &code, const datum::GeodeticReferenceFrameNNPtr &datum) { insertIntoCache(cacheGeodeticDatum_, code, datum.as_nullable()); } // --------------------------------------------------------------------------- datum::EllipsoidPtr DatabaseContext::Private::getEllipsoidFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheEllipsoid_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const datum::EllipsoidNNPtr &ellps) { insertIntoCache(cacheEllipsoid_, code, ellps.as_nullable()); } // --------------------------------------------------------------------------- datum::PrimeMeridianPtr DatabaseContext::Private::getPrimeMeridianFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cachePrimeMeridian_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const datum::PrimeMeridianNNPtr &pm) { insertIntoCache(cachePrimeMeridian_, code, pm.as_nullable()); } // --------------------------------------------------------------------------- cs::CoordinateSystemPtr DatabaseContext::Private::getCoordinateSystemFromCache( const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheCS_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const cs::CoordinateSystemNNPtr &cs) { insertIntoCache(cacheCS_, code, cs.as_nullable()); } // --------------------------------------------------------------------------- metadata::ExtentPtr DatabaseContext::Private::getExtentFromCache(const std::string &code) { util::BaseObjectPtr obj; getFromCache(cacheExtent_, code, obj); return std::static_pointer_cast(obj); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const metadata::ExtentNNPtr &extent) { insertIntoCache(cacheExtent_, code, extent.as_nullable()); } // --------------------------------------------------------------------------- bool DatabaseContext::Private::getGridInfoFromCache(const std::string &code, GridInfoCache &info) { return cacheGridInfo_.tryGet(code, info); } // --------------------------------------------------------------------------- void DatabaseContext::Private::cache(const std::string &code, const GridInfoCache &info) { cacheGridInfo_.insert(code, info); } // --------------------------------------------------------------------------- #ifdef ENABLE_CUSTOM_LOCKLESS_VFS typedef int (*ClosePtr)(sqlite3_file *); static int VFSClose(sqlite3_file *file) { sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); assert(defaultVFS); ClosePtr defaultClosePtr; std::memcpy(&defaultClosePtr, reinterpret_cast(file) + defaultVFS->szOsFile, sizeof(ClosePtr)); void *methods = const_cast(file->pMethods); int ret = defaultClosePtr(file); std::free(methods); return ret; } // No-lock implementation static int VSFLock(sqlite3_file *, int) { return SQLITE_OK; } static int VSFUnlock(sqlite3_file *, int) { return SQLITE_OK; } static int VFSOpen(sqlite3_vfs *vfs, const char *name, sqlite3_file *file, int flags, int *outFlags) { sqlite3_vfs *defaultVFS = static_cast(vfs->pAppData); int ret = defaultVFS->xOpen(defaultVFS, name, file, flags, outFlags); if (ret == SQLITE_OK) { ClosePtr defaultClosePtr = file->pMethods->xClose; assert(defaultClosePtr); sqlite3_io_methods *methods = static_cast( std::malloc(sizeof(sqlite3_io_methods))); if (!methods) { file->pMethods->xClose(file); return SQLITE_NOMEM; } memcpy(methods, file->pMethods, sizeof(sqlite3_io_methods)); methods->xClose = VFSClose; methods->xLock = VSFLock; methods->xUnlock = VSFUnlock; file->pMethods = methods; // Save original xClose pointer at end of file structure std::memcpy(reinterpret_cast(file) + defaultVFS->szOsFile, &defaultClosePtr, sizeof(ClosePtr)); } return ret; } static int VFSAccess(sqlite3_vfs *vfs, const char *zName, int flags, int *pResOut) { sqlite3_vfs *defaultVFS = static_cast(vfs->pAppData); // Do not bother stat'ing for journal or wal files if (std::strstr(zName, "-journal") || std::strstr(zName, "-wal")) { *pResOut = false; return SQLITE_OK; } return defaultVFS->xAccess(defaultVFS, zName, flags, pResOut); } // --------------------------------------------------------------------------- bool DatabaseContext::Private::createCustomVFS() { sqlite3_vfs *defaultVFS = sqlite3_vfs_find(nullptr); assert(defaultVFS); std::ostringstream buffer; buffer << this; thisNamePtr_ = buffer.str(); vfs_ = new sqlite3_vfs(); vfs_->iVersion = 1; vfs_->szOsFile = defaultVFS->szOsFile + sizeof(ClosePtr); vfs_->mxPathname = defaultVFS->mxPathname; vfs_->zName = thisNamePtr_.c_str(); vfs_->pAppData = defaultVFS; vfs_->xOpen = VFSOpen; vfs_->xDelete = defaultVFS->xDelete; vfs_->xAccess = VFSAccess; vfs_->xFullPathname = defaultVFS->xFullPathname; vfs_->xDlOpen = defaultVFS->xDlOpen; vfs_->xDlError = defaultVFS->xDlError; vfs_->xDlSym = defaultVFS->xDlSym; vfs_->xDlClose = defaultVFS->xDlClose; vfs_->xRandomness = defaultVFS->xRandomness; vfs_->xSleep = defaultVFS->xSleep; vfs_->xCurrentTime = defaultVFS->xCurrentTime; vfs_->xGetLastError = defaultVFS->xGetLastError; vfs_->xCurrentTimeInt64 = defaultVFS->xCurrentTimeInt64; return sqlite3_vfs_register(vfs_, false) == SQLITE_OK; } #endif // ENABLE_CUSTOM_LOCKLESS_VFS // --------------------------------------------------------------------------- void DatabaseContext::Private::open(const std::string &databasePath, PJ_CONTEXT *ctx) { setPjCtxt(ctx ? ctx : pj_get_default_ctx()); std::string path(databasePath); if (path.empty()) { path.resize(2048); const bool found = pj_find_file(pjCtxt(), "proj.db", &path[0], path.size() - 1) != 0; path.resize(strlen(path.c_str())); if (!found) { throw FactoryException("Cannot find proj.db"); } } if ( #ifdef ENABLE_CUSTOM_LOCKLESS_VFS !createCustomVFS() || #endif sqlite3_open_v2(path.c_str(), &sqlite_handle_, SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX, #ifdef ENABLE_CUSTOM_LOCKLESS_VFS thisNamePtr_.c_str() #else nullptr #endif ) != SQLITE_OK || !sqlite_handle_) { throw FactoryException("Open of " + path + " failed"); } databasePath_ = path; registerFunctions(); } // --------------------------------------------------------------------------- void DatabaseContext::Private::setHandle(sqlite3 *sqlite_handle) { assert(sqlite_handle); assert(!sqlite_handle_); sqlite_handle_ = sqlite_handle; close_handle_ = false; registerFunctions(); } // --------------------------------------------------------------------------- std::vector DatabaseContext::Private::getDatabaseStructure() { const char *sqls[] = { "SELECT sql FROM sqlite_master WHERE type = 'table'", "SELECT sql FROM sqlite_master WHERE type = 'view'", "SELECT sql FROM sqlite_master WHERE type = 'trigger'"}; std::vector res; for (const auto &sql : sqls) { auto sqlRes = run(sql); for (const auto &row : sqlRes) { res.emplace_back(row[0]); } } return res; } // --------------------------------------------------------------------------- void DatabaseContext::Private::attachExtraDatabases( const std::vector &auxiliaryDatabasePaths) { assert(close_handle_); assert(sqlite_handle_); auto tables = run("SELECT name FROM sqlite_master WHERE type IN ('table', 'view')"); std::map> tableStructure; for (const auto &rowTable : tables) { auto tableName = rowTable[0]; auto tableInfo = run("PRAGMA table_info(\"" + replaceAll(tableName, "\"", "\"\"") + "\")"); for (const auto &rowCol : tableInfo) { const auto &colName = rowCol[1]; tableStructure[tableName].push_back(colName); } } closeDB(); sqlite3_open_v2(":memory:", &sqlite_handle_, SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX #ifdef SQLITE_OPEN_URI | SQLITE_OPEN_URI #endif , nullptr); if (!sqlite_handle_) { throw FactoryException("cannot create in memory database"); } run("ATTACH DATABASE '" + replaceAll(databasePath_, "'", "''") + "' AS db_0"); detach_ = true; int count = 1; for (const auto &otherDb : auxiliaryDatabasePaths) { std::string sql = "ATTACH DATABASE '"; sql += replaceAll(otherDb, "'", "''"); sql += "' AS db_"; sql += toString(static_cast(count)); count++; run(sql); } for (const auto &pair : tableStructure) { std::string sql("CREATE TEMP VIEW "); sql += pair.first; sql += " AS "; for (size_t i = 0; i <= auxiliaryDatabasePaths.size(); ++i) { std::string selectFromAux("SELECT "); bool firstCol = true; for (const auto &colName : pair.second) { if (!firstCol) { selectFromAux += ", "; } firstCol = false; selectFromAux += colName; } selectFromAux += " FROM db_"; selectFromAux += toString(static_cast(i)); selectFromAux += "."; selectFromAux += pair.first; try { // Check that the request will succeed. In case of 'sparse' // databases... run(selectFromAux + " LIMIT 0"); if (i > 0) { sql += " UNION ALL "; } sql += selectFromAux; } catch (const std::exception &) { } } run(sql); } registerFunctions(); } // --------------------------------------------------------------------------- static double PROJ_SQLITE_GetValAsDouble(sqlite3_value *val, bool &gotVal) { switch (sqlite3_value_type(val)) { case SQLITE_FLOAT: gotVal = true; return sqlite3_value_double(val); case SQLITE_INTEGER: gotVal = true; return static_cast(sqlite3_value_int64(val)); default: gotVal = false; return 0.0; } } // --------------------------------------------------------------------------- static void PROJ_SQLITE_pseudo_area_from_swne(sqlite3_context *pContext, int /* argc */, sqlite3_value **argv) { bool b0, b1, b2, b3; double south_lat = PROJ_SQLITE_GetValAsDouble(argv[0], b0); double west_lon = PROJ_SQLITE_GetValAsDouble(argv[1], b1); double north_lat = PROJ_SQLITE_GetValAsDouble(argv[2], b2); double east_lon = PROJ_SQLITE_GetValAsDouble(argv[3], b3); if (!b0 || !b1 || !b2 || !b3) { sqlite3_result_null(pContext); return; } // Deal with area crossing antimeridian if (east_lon < west_lon) { east_lon += 360.0; } // Integrate cos(lat) between south_lat and north_lat double pseudo_area = (east_lon - west_lon) * (std::sin(common::Angle(north_lat).getSIValue()) - std::sin(common::Angle(south_lat).getSIValue())); sqlite3_result_double(pContext, pseudo_area); } // --------------------------------------------------------------------------- static void PROJ_SQLITE_intersects_bbox(sqlite3_context *pContext, int /* argc */, sqlite3_value **argv) { bool b0, b1, b2, b3, b4, b5, b6, b7; double south_lat1 = PROJ_SQLITE_GetValAsDouble(argv[0], b0); double west_lon1 = PROJ_SQLITE_GetValAsDouble(argv[1], b1); double north_lat1 = PROJ_SQLITE_GetValAsDouble(argv[2], b2); double east_lon1 = PROJ_SQLITE_GetValAsDouble(argv[3], b3); double south_lat2 = PROJ_SQLITE_GetValAsDouble(argv[4], b4); double west_lon2 = PROJ_SQLITE_GetValAsDouble(argv[5], b5); double north_lat2 = PROJ_SQLITE_GetValAsDouble(argv[6], b6); double east_lon2 = PROJ_SQLITE_GetValAsDouble(argv[7], b7); if (!b0 || !b1 || !b2 || !b3 || !b4 || !b5 || !b6 || !b7) { sqlite3_result_null(pContext); return; } auto bbox1 = metadata::GeographicBoundingBox::create(west_lon1, south_lat1, east_lon1, north_lat1); auto bbox2 = metadata::GeographicBoundingBox::create(west_lon2, south_lat2, east_lon2, north_lat2); sqlite3_result_int(pContext, bbox1->intersects(bbox2) ? 1 : 0); } // --------------------------------------------------------------------------- #ifndef SQLITE_DETERMINISTIC #define SQLITE_DETERMINISTIC 0 #endif void DatabaseContext::Private::registerFunctions() { sqlite3_create_function(sqlite_handle_, "pseudo_area_from_swne", 4, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, PROJ_SQLITE_pseudo_area_from_swne, nullptr, nullptr); sqlite3_create_function(sqlite_handle_, "intersects_bbox", 8, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr, PROJ_SQLITE_intersects_bbox, nullptr, nullptr); } // --------------------------------------------------------------------------- SQLResultSet DatabaseContext::Private::run(const std::string &sql, const ListOfParams ¶meters) { sqlite3_stmt *stmt = nullptr; auto iter = mapSqlToStatement_.find(sql); if (iter != mapSqlToStatement_.end()) { stmt = iter->second; sqlite3_reset(stmt); } else { if (sqlite3_prepare_v2(sqlite_handle_, sql.c_str(), static_cast(sql.size()), &stmt, nullptr) != SQLITE_OK) { throw FactoryException("SQLite error on " + sql + ": " + sqlite3_errmsg(sqlite_handle_)); } mapSqlToStatement_.insert( std::pair(sql, stmt)); } int nBindField = 1; for (const auto ¶m : parameters) { if (param.type() == SQLValues::Type::STRING) { auto strValue = param.stringValue(); sqlite3_bind_text(stmt, nBindField, strValue.c_str(), static_cast(strValue.size()), SQLITE_TRANSIENT); } else { assert(param.type() == SQLValues::Type::DOUBLE); sqlite3_bind_double(stmt, nBindField, param.doubleValue()); } nBindField++; } #ifdef TRACE_DATABASE size_t nPos = 0; std::string sqlSubst(sql); for (const auto ¶m : parameters) { nPos = sqlSubst.find('?', nPos); assert(nPos != std::string::npos); std::string strValue; if (param.type() == SQLValues::Type::STRING) { strValue = '\'' + param.stringValue() + '\''; } else { strValue = toString(param.doubleValue()); } sqlSubst = sqlSubst.substr(0, nPos) + strValue + sqlSubst.substr(nPos + 1); nPos += strValue.size(); } logTrace(sqlSubst, "DATABASE"); #endif SQLResultSet result; const int column_count = sqlite3_column_count(stmt); while (true) { int ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { SQLRow row(column_count); for (int i = 0; i < column_count; i++) { const char *txt = reinterpret_cast( sqlite3_column_text(stmt, i)); if (txt) { row[i] = txt; } } result.emplace_back(std::move(row)); } else if (ret == SQLITE_DONE) { break; } else { throw FactoryException("SQLite error on " + sql + ": " + sqlite3_errmsg(sqlite_handle_)); } } return result; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DatabaseContext::~DatabaseContext() = default; //! @endcond // --------------------------------------------------------------------------- DatabaseContext::DatabaseContext() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- /** \brief Instantiate a database context. * * This database context should be used only by one thread at a time. * * @param databasePath Path and filename of the database. Might be empty * string for the default rules to locate the default proj.db * @param auxiliaryDatabasePaths Path and filename of auxiliary databases. * Might be empty. * @param ctx Context used for file search. * @throw FactoryException */ DatabaseContextNNPtr DatabaseContext::create(const std::string &databasePath, const std::vector &auxiliaryDatabasePaths, PJ_CONTEXT *ctx) { auto dbCtx = DatabaseContext::nn_make_shared(); dbCtx->getPrivate()->open(databasePath, ctx); if (!auxiliaryDatabasePaths.empty()) { dbCtx->getPrivate()->attachExtraDatabases(auxiliaryDatabasePaths); } return dbCtx; } // --------------------------------------------------------------------------- /** \brief Return the list of authorities used in the database. */ std::set DatabaseContext::getAuthorities() const { auto res = d->run("SELECT auth_name FROM authority_list"); std::set list; for (const auto &row : res) { list.insert(row[0]); } return list; } // --------------------------------------------------------------------------- /** \brief Return the list of SQL commands (CREATE TABLE, CREATE TRIGGER, * CREATE VIEW) needed to initialize a new database. */ std::vector DatabaseContext::getDatabaseStructure() const { return d->getDatabaseStructure(); } // --------------------------------------------------------------------------- /** \brief Return the path to the database. */ const std::string &DatabaseContext::getPath() const { return d->getPath(); } // --------------------------------------------------------------------------- /** \brief Return a metadata item. * * Value remains valid while this is alive and to the next call to getMetadata */ const char *DatabaseContext::getMetadata(const char *key) const { auto res = d->run("SELECT value FROM metadata WHERE key = ?", {std::string(key)}); if (res.empty()) { return nullptr; } d->lastMetadataValue_ = res.front()[0]; return d->lastMetadataValue_.c_str(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DatabaseContextNNPtr DatabaseContext::create(void *sqlite_handle) { auto ctxt = DatabaseContext::nn_make_shared(); ctxt->getPrivate()->setHandle(static_cast(sqlite_handle)); return ctxt; } // --------------------------------------------------------------------------- void *DatabaseContext::getSqliteHandle() const { return getPrivate()->handle(); } // --------------------------------------------------------------------------- bool DatabaseContext::lookForGridAlternative(const std::string &officialName, std::string &projFilename, std::string &projFormat, bool &inverse) const { auto res = d->run( "SELECT proj_grid_name, proj_grid_format, inverse_direction FROM " "grid_alternatives WHERE original_grid_name = ?", {officialName}); if (res.empty()) { return false; } const auto &row = res.front(); projFilename = row[0]; projFormat = row[1]; inverse = row[2] == "1"; return true; } // --------------------------------------------------------------------------- bool DatabaseContext::lookForGridInfo(const std::string &projFilename, std::string &fullFilename, std::string &packageName, std::string &url, bool &directDownload, bool &openLicense, bool &gridAvailable) const { Private::GridInfoCache info; if (d->getGridInfoFromCache(projFilename, info)) { fullFilename = info.fullFilename; packageName = info.packageName; url = info.url; directDownload = info.directDownload; openLicense = info.openLicense; gridAvailable = info.gridAvailable; return info.found; } fullFilename.clear(); packageName.clear(); url.clear(); openLicense = false; directDownload = false; fullFilename.resize(2048); if (d->pjCtxt() == nullptr) { d->setPjCtxt(pj_get_default_ctx()); } int errno_before = proj_context_errno(d->pjCtxt()); gridAvailable = pj_find_file(d->pjCtxt(), projFilename.c_str(), &fullFilename[0], fullFilename.size() - 1) != 0; proj_context_errno_set(d->pjCtxt(), errno_before); fullFilename.resize(strlen(fullFilename.c_str())); auto res = d->run("SELECT " "grid_packages.package_name, " "grid_alternatives.url, " "grid_packages.url AS package_url, " "grid_alternatives.open_license, " "grid_packages.open_license AS package_open_license, " "grid_alternatives.direct_download, " "grid_packages.direct_download AS package_direct_download " "FROM grid_alternatives " "LEFT JOIN grid_packages ON " "grid_alternatives.package_name = grid_packages.package_name " "WHERE proj_grid_name = ?", {projFilename}); bool ret = !res.empty(); if (ret) { const auto &row = res.front(); packageName = std::move(row[0]); url = row[1].empty() ? std::move(row[2]) : std::move(row[1]); openLicense = (row[3].empty() ? row[4] : row[3]) == "1"; directDownload = (row[5].empty() ? row[6] : row[5]) == "1"; info.fullFilename = fullFilename; info.packageName = packageName; info.url = url; info.directDownload = directDownload; info.openLicense = openLicense; } info.gridAvailable = gridAvailable; info.found = ret; d->cache(projFilename, info); return ret; } // --------------------------------------------------------------------------- bool DatabaseContext::isKnownName(const std::string &name, const std::string &tableName) const { std::string sql("SELECT 1 FROM \""); sql += replaceAll(tableName, "\"", "\"\""); sql += "\" WHERE name = ? LIMIT 1"; return !d->run(sql, {name}).empty(); } // --------------------------------------------------------------------------- /** \brief Gets the alias name from an official name. * * @param officialName Official name. Mandatory * @param tableName Table name/category. Mandatory * @param source Source of the alias. Mandatory * @return Alias name (or empty if not found). * @throw FactoryException */ std::string DatabaseContext::getAliasFromOfficialName(const std::string &officialName, const std::string &tableName, const std::string &source) const { std::string sql("SELECT auth_name, code FROM \""); sql += replaceAll(tableName, "\"", "\"\""); sql += "\" WHERE name = ?"; if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } auto res = d->run(sql, {officialName}); if (res.empty()) { return std::string(); } const auto &row = res.front(); res = d->run("SELECT alt_name FROM alias_name WHERE table_name = ? AND " "auth_name = ? AND code = ? AND source = ?", {tableName, row[0], row[1], source}); if (res.empty()) { return std::string(); } return res.front()[0]; } // --------------------------------------------------------------------------- /** \brief Gets the alias names for an object. * * Either authName + code or officialName must be non empty. * * @param authName Authority. * @param code Code. * @param officialName Official name. * @param tableName Table name/category. Mandatory * @param source Source of the alias. May be empty. * @return Aliases */ std::list DatabaseContext::getAliases( const std::string &authName, const std::string &code, const std::string &officialName, const std::string &tableName, const std::string &source) const { std::list res; const auto key(authName + code + officialName + tableName + source); if (d->cacheAliasNames_.tryGet(key, res)) { return res; } std::string resolvedAuthName(authName); std::string resolvedCode(code); if (authName.empty() || code.empty()) { std::string sql("SELECT auth_name, code FROM \""); sql += replaceAll(tableName, "\"", "\"\""); sql += "\" WHERE name = ?"; if (tableName == "geodetic_crs") { sql += " AND type = " GEOG_2D_SINGLE_QUOTED; } auto resSql = d->run(sql, {officialName}); if (resSql.empty()) { d->cacheAliasNames_.insert(key, res); return res; } const auto &row = resSql.front(); resolvedAuthName = row[0]; resolvedCode = row[1]; } std::string sql("SELECT alt_name FROM alias_name WHERE table_name = ? AND " "auth_name = ? AND code = ?"); ListOfParams params{tableName, resolvedAuthName, resolvedCode}; if (!source.empty()) { sql += " AND source = ?"; params.emplace_back(source); } auto resSql = d->run(sql, params); for (const auto &row : resSql) { res.emplace_back(row[0]); } d->cacheAliasNames_.insert(key, res); return res; } // --------------------------------------------------------------------------- /** \brief Return the 'text_definition' column of a table for an object * * @param tableName Table name/category. * @param authName Authority name of the object. * @param code Code of the object * @return Text definition (or empty) * @throw FactoryException */ std::string DatabaseContext::getTextDefinition(const std::string &tableName, const std::string &authName, const std::string &code) const { std::string sql("SELECT text_definition FROM \""); sql += replaceAll(tableName, "\"", "\"\""); sql += "\" WHERE auth_name = ? AND code = ?"; auto res = d->run(sql, {authName, code}); if (res.empty()) { return std::string(); } return res.front()[0]; } // --------------------------------------------------------------------------- /** \brief Return the allowed authorities when researching transformations * between different authorities. * * @throw FactoryException */ std::vector DatabaseContext::getAllowedAuthorities( const std::string &sourceAuthName, const std::string &targetAuthName) const { const auto key(sourceAuthName + targetAuthName); auto hit = d->cacheAllowedAuthorities_.find(key); if (hit != d->cacheAllowedAuthorities_.end()) { return hit->second; } auto sqlRes = d->run( "SELECT allowed_authorities FROM authority_to_authority_preference " "WHERE source_auth_name = ? AND target_auth_name = ?", {sourceAuthName, targetAuthName}); if (sqlRes.empty()) { sqlRes = d->run( "SELECT allowed_authorities FROM authority_to_authority_preference " "WHERE source_auth_name = ? AND target_auth_name = 'any'", {sourceAuthName}); } if (sqlRes.empty()) { sqlRes = d->run( "SELECT allowed_authorities FROM authority_to_authority_preference " "WHERE source_auth_name = 'any' AND target_auth_name = ?", {targetAuthName}); } if (sqlRes.empty()) { sqlRes = d->run( "SELECT allowed_authorities FROM authority_to_authority_preference " "WHERE source_auth_name = 'any' AND target_auth_name = 'any'", {}); } if (sqlRes.empty()) { d->cacheAllowedAuthorities_[key] = std::vector(); return std::vector(); } auto res = split(sqlRes.front()[0], ','); d->cacheAllowedAuthorities_[key] = res; return res; } // --------------------------------------------------------------------------- std::list> DatabaseContext::getNonDeprecated(const std::string &tableName, const std::string &authName, const std::string &code) const { auto sqlRes = d->run("SELECT replacement_auth_name, replacement_code, source " "FROM deprecation " "WHERE table_name = ? AND deprecated_auth_name = ? " "AND deprecated_code = ?", {tableName, authName, code}); std::list> res; for (const auto &row : sqlRes) { const auto &source = row[2]; if (source == "PROJ") { const auto &replacement_auth_name = row[0]; const auto &replacement_code = row[1]; res.emplace_back(replacement_auth_name, replacement_code); } } if (!res.empty()) { return res; } for (const auto &row : sqlRes) { const auto &replacement_auth_name = row[0]; const auto &replacement_code = row[1]; res.emplace_back(replacement_auth_name, replacement_code); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct AuthorityFactory::Private { Private(const DatabaseContextNNPtr &contextIn, const std::string &authorityName) : context_(contextIn), authority_(authorityName) {} inline const std::string &authority() PROJ_PURE_DEFN { return authority_; } inline const DatabaseContextNNPtr &context() PROJ_PURE_DEFN { return context_; } // cppcheck-suppress functionStatic void setThis(AuthorityFactoryNNPtr factory) { thisFactory_ = factory.as_nullable(); } // cppcheck-suppress functionStatic AuthorityFactoryPtr getSharedFromThis() { return thisFactory_.lock(); } inline AuthorityFactoryNNPtr createFactory(const std::string &auth_name) { if (auth_name == authority_) { return NN_NO_CHECK(thisFactory_.lock()); } return AuthorityFactory::create(context_, auth_name); } bool rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op); UnitOfMeasure createUnitOfMeasure(const std::string &auth_name, const std::string &code); util::PropertyMap createProperties(const std::string &code, const std::string &name, bool deprecated, const metadata::ExtentPtr &extent); util::PropertyMap createProperties(const std::string &code, const std::string &name, bool deprecated, const std::string &area_of_use_auth_name, const std::string &area_of_use_code); util::PropertyMap createProperties(const std::string &code, const std::string &name, bool deprecated, const std::string &remarks, const std::string &scope, const std::string &area_of_use_auth_name, const std::string &area_of_use_code); SQLResultSet run(const std::string &sql, const ListOfParams ¶meters = ListOfParams()); SQLResultSet runWithCodeParam(const std::string &sql, const std::string &code); SQLResultSet runWithCodeParam(const char *sql, const std::string &code); bool hasAuthorityRestriction() const { return !authority_.empty() && authority_ != "any"; } private: DatabaseContextNNPtr context_; std::string authority_; std::weak_ptr thisFactory_{}; }; // --------------------------------------------------------------------------- SQLResultSet AuthorityFactory::Private::run(const std::string &sql, const ListOfParams ¶meters) { return context()->getPrivate()->run(sql, parameters); } // --------------------------------------------------------------------------- SQLResultSet AuthorityFactory::Private::runWithCodeParam(const std::string &sql, const std::string &code) { return run(sql, {authority(), code}); } // --------------------------------------------------------------------------- SQLResultSet AuthorityFactory::Private::runWithCodeParam(const char *sql, const std::string &code) { return runWithCodeParam(std::string(sql), code); } // --------------------------------------------------------------------------- UnitOfMeasure AuthorityFactory::Private::createUnitOfMeasure(const std::string &auth_name, const std::string &code) { return *(createFactory(auth_name)->createUnitOfMeasure(code)); } // --------------------------------------------------------------------------- util::PropertyMap AuthorityFactory::Private::createProperties( const std::string &code, const std::string &name, bool deprecated, const metadata::ExtentPtr &extent) { auto props = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, authority()) .set(metadata::Identifier::CODE_KEY, code) .set(common::IdentifiedObject::NAME_KEY, name); if (deprecated) { props.set(common::IdentifiedObject::DEPRECATED_KEY, true); } if (extent) { props.set( common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, NN_NO_CHECK(std::static_pointer_cast(extent))); } return props; } // --------------------------------------------------------------------------- util::PropertyMap AuthorityFactory::Private::createProperties( const std::string &code, const std::string &name, bool deprecated, const std::string &area_of_use_auth_name, const std::string &area_of_use_code) { return createProperties(code, name, deprecated, area_of_use_auth_name.empty() ? nullptr : createFactory(area_of_use_auth_name) ->createExtent(area_of_use_code) .as_nullable()); } // --------------------------------------------------------------------------- util::PropertyMap AuthorityFactory::Private::createProperties( const std::string &code, const std::string &name, bool deprecated, const std::string &remarks, const std::string &scope, const std::string &area_of_use_auth_name, const std::string &area_of_use_code) { auto props = createProperties(code, name, deprecated, area_of_use_auth_name.empty() ? nullptr : createFactory(area_of_use_auth_name) ->createExtent(area_of_use_code) .as_nullable()); if (!remarks.empty()) props.set(common::IdentifiedObject::REMARKS_KEY, remarks); if (!scope.empty()) props.set(common::ObjectUsage::SCOPE_KEY, scope); return props; } // --------------------------------------------------------------------------- bool AuthorityFactory::Private::rejectOpDueToMissingGrid( const operation::CoordinateOperationNNPtr &op) { for (const auto &gridDesc : op->gridsNeeded(context())) { if (!gridDesc.available) { return true; } } return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress AuthorityFactory::~AuthorityFactory() = default; //! @endcond // --------------------------------------------------------------------------- AuthorityFactory::AuthorityFactory(const DatabaseContextNNPtr &context, const std::string &authorityName) : d(internal::make_unique(context, authorityName)) {} // --------------------------------------------------------------------------- // clang-format off /** \brief Instantiate a AuthorityFactory. * * The authority name might be set to the empty string in the particular case * where createFromCoordinateReferenceSystemCodes(const std::string&,const std::string&,const std::string&,const std::string&) const * is called. * * @param context Contexte. * @param authorityName Authority name. * @return new AuthorityFactory. */ // clang-format on AuthorityFactoryNNPtr AuthorityFactory::create(const DatabaseContextNNPtr &context, const std::string &authorityName) { const auto getFactory = [&context, &authorityName]() { for (const auto &knownName : {"EPSG", "ESRI", "PROJ"}) { if (ci_equal(authorityName, knownName)) { return AuthorityFactory::nn_make_shared( context, knownName); } } return AuthorityFactory::nn_make_shared( context, authorityName); }; auto factory = getFactory(); factory->d->setThis(factory); return factory; } // --------------------------------------------------------------------------- /** \brief Returns the database context. */ const DatabaseContextNNPtr &AuthorityFactory::databaseContext() const { return d->context(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress AuthorityFactory::CRSInfo::CRSInfo() : authName{}, code{}, name{}, type{ObjectType::CRS}, deprecated{}, bbox_valid{}, west_lon_degree{}, south_lat_degree{}, east_lon_degree{}, north_lat_degree{}, areaName{}, projectionMethodName{} {} //! @endcond // --------------------------------------------------------------------------- /** \brief Returns an arbitrary object from a code. * * The returned object will typically be an instance of Datum, * CoordinateSystem, ReferenceSystem or CoordinateOperation. If the type of * the object is know at compile time, it is recommended to invoke the most * precise method instead of this one (for example * createCoordinateReferenceSystem(code) instead of createObject(code) * if the caller know he is asking for a coordinate reference system). * * If there are several objects with the same code, a FactoryException is * thrown. * * @param code Object code allocated by authority. (e.g. "4326") * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ util::BaseObjectNNPtr AuthorityFactory::createObject(const std::string &code) const { auto res = d->runWithCodeParam( "SELECT table_name FROM object_view WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("not found", d->authority(), code); } if (res.size() != 1) { std::string msg( "More than one object matching specified code. Objects found in "); bool first = true; for (const auto &row : res) { if (!first) msg += ", "; msg += row[0]; first = false; } throw FactoryException(msg); } const auto &table_name = res.front()[0]; if (table_name == "area") { return util::nn_static_pointer_cast( createExtent(code)); } if (table_name == "unit_of_measure") { return util::nn_static_pointer_cast( createUnitOfMeasure(code)); } if (table_name == "prime_meridian") { return util::nn_static_pointer_cast( createPrimeMeridian(code)); } if (table_name == "ellipsoid") { return util::nn_static_pointer_cast( createEllipsoid(code)); } if (table_name == "geodetic_datum") { return util::nn_static_pointer_cast( createGeodeticDatum(code)); } if (table_name == "vertical_datum") { return util::nn_static_pointer_cast( createVerticalDatum(code)); } if (table_name == "geodetic_crs") { return util::nn_static_pointer_cast( createGeodeticCRS(code)); } if (table_name == "vertical_crs") { return util::nn_static_pointer_cast( createVerticalCRS(code)); } if (table_name == "projected_crs") { return util::nn_static_pointer_cast( createProjectedCRS(code)); } if (table_name == "compound_crs") { return util::nn_static_pointer_cast( createCompoundCRS(code)); } if (table_name == "conversion") { return util::nn_static_pointer_cast( createConversion(code)); } if (table_name == "helmert_transformation" || table_name == "grid_transformation" || table_name == "other_transformation" || table_name == "concatenated_operation") { return util::nn_static_pointer_cast( createCoordinateOperation(code, false)); } throw FactoryException("unimplemented factory for " + res.front()[0]); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static FactoryException buildFactoryException(const char *type, const std::string &code, const std::exception &ex) { return FactoryException(std::string("cannot build ") + type + " " + code + ": " + ex.what()); } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns a metadata::Extent from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ metadata::ExtentNNPtr AuthorityFactory::createExtent(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto extent = d->context()->d->getExtentFromCache(cacheKey); if (extent) { return NN_NO_CHECK(extent); } } auto sql = "SELECT name, south_lat, north_lat, west_lon, east_lon, " "deprecated FROM area WHERE auth_name = ? AND code = ?"; auto res = d->runWithCodeParam(sql, code); if (res.empty()) { throw NoSuchAuthorityCodeException("area not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; if (row[1].empty()) { auto extent = metadata::Extent::create( util::optional(name), {}, {}, {}); d->context()->d->cache(cacheKey, extent); return extent; } double south_lat = c_locale_stod(row[1]); double north_lat = c_locale_stod(row[2]); double west_lon = c_locale_stod(row[3]); double east_lon = c_locale_stod(row[4]); auto bbox = metadata::GeographicBoundingBox::create( west_lon, south_lat, east_lon, north_lat); auto extent = metadata::Extent::create( util::optional(name), std::vector{bbox}, std::vector(), std::vector()); d->context()->d->cache(cacheKey, extent); return extent; } catch (const std::exception &ex) { throw buildFactoryException("area", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a common::UnitOfMeasure from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ UnitOfMeasureNNPtr AuthorityFactory::createUnitOfMeasure(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto uom = d->context()->d->getUOMFromCache(cacheKey); if (uom) { return NN_NO_CHECK(uom); } } auto res = d->runWithCodeParam( "SELECT name, conv_factor, type, deprecated FROM unit_of_measure WHERE " "auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("unit of measure not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = (row[0] == "degree (supplier to define representation)") ? UnitOfMeasure::DEGREE.name() : row[0]; double conv_factor = (code == "9107" || code == "9108") ? UnitOfMeasure::DEGREE.conversionToSI() : c_locale_stod(row[1]); constexpr double EPS = 1e-10; if (std::fabs(conv_factor - UnitOfMeasure::DEGREE.conversionToSI()) < EPS * UnitOfMeasure::DEGREE.conversionToSI()) { conv_factor = UnitOfMeasure::DEGREE.conversionToSI(); } if (std::fabs(conv_factor - UnitOfMeasure::ARC_SECOND.conversionToSI()) < EPS * UnitOfMeasure::ARC_SECOND.conversionToSI()) { conv_factor = UnitOfMeasure::ARC_SECOND.conversionToSI(); } const auto &type_str = row[2]; UnitOfMeasure::Type unitType = UnitOfMeasure::Type::UNKNOWN; if (type_str == "length") unitType = UnitOfMeasure::Type::LINEAR; else if (type_str == "angle") unitType = UnitOfMeasure::Type::ANGULAR; else if (type_str == "scale") unitType = UnitOfMeasure::Type::SCALE; else if (type_str == "time") unitType = UnitOfMeasure::Type::TIME; auto uom = util::nn_make_shared( name, conv_factor, unitType, d->authority(), code); d->context()->d->cache(cacheKey, uom); return uom; } catch (const std::exception &ex) { throw buildFactoryException("unit of measure", code, ex); } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static double normalizeMeasure(const std::string &uom_code, const std::string &value, std::string &normalized_uom_code) { if (uom_code == "9110") // DDD.MMSSsss..... { double normalized_value = c_locale_stod(value); std::ostringstream buffer; buffer.imbue(std::locale::classic()); constexpr size_t precision = 12; buffer << std::fixed << std::setprecision(precision) << normalized_value; auto formatted = buffer.str(); size_t dotPos = formatted.find('.'); assert(dotPos + 1 + precision == formatted.size()); auto minutes = formatted.substr(dotPos + 1, 2); auto seconds = formatted.substr(dotPos + 3); assert(seconds.size() == precision - 2); normalized_value = (normalized_value < 0 ? -1.0 : 1.0) * (std::floor(std::fabs(normalized_value)) + c_locale_stod(minutes) / 60. + (c_locale_stod(seconds) / std::pow(10, seconds.size() - 2)) / 3600.); normalized_uom_code = common::UnitOfMeasure::DEGREE.code(); /* coverity[overflow_sink] */ return normalized_value; } else { normalized_uom_code = uom_code; return c_locale_stod(value); } } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns a datum::PrimeMeridian from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ datum::PrimeMeridianNNPtr AuthorityFactory::createPrimeMeridian(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto pm = d->context()->d->getPrimeMeridianFromCache(cacheKey); if (pm) { return NN_NO_CHECK(pm); } } auto res = d->runWithCodeParam( "SELECT name, longitude, uom_auth_name, uom_code, deprecated FROM " "prime_meridian WHERE " "auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("prime meridian not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &longitude = row[1]; const auto &uom_auth_name = row[2]; const auto &uom_code = row[3]; const bool deprecated = row[4] == "1"; std::string normalized_uom_code(uom_code); const double normalized_value = normalizeMeasure(uom_code, longitude, normalized_uom_code); auto uom = d->createUnitOfMeasure(uom_auth_name, normalized_uom_code); auto props = d->createProperties(code, name, deprecated, nullptr); auto pm = datum::PrimeMeridian::create( props, common::Angle(normalized_value, uom)); d->context()->d->cache(cacheKey, pm); return pm; } catch (const std::exception &ex) { throw buildFactoryException("prime meridian", code, ex); } } // --------------------------------------------------------------------------- /** \brief Identify a celestial body from an approximate radius. * * @param semi_major_axis Approximate semi-major axis. * @param tolerance Relative error allowed. * @return celestial body name if one single match found. * @throw FactoryException */ std::string AuthorityFactory::identifyBodyFromSemiMajorAxis(double semi_major_axis, double tolerance) const { auto res = d->run("SELECT name, (ABS(semi_major_axis - ?) / semi_major_axis ) " "AS rel_error FROM celestial_body WHERE rel_error <= ?", {semi_major_axis, tolerance}); if (res.empty()) { throw FactoryException("no match found"); } if (res.size() > 1) { throw FactoryException("more than one match found"); } return res.front()[0]; } // --------------------------------------------------------------------------- /** \brief Returns a datum::Ellipsoid from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ datum::EllipsoidNNPtr AuthorityFactory::createEllipsoid(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto ellps = d->context()->d->getEllipsoidFromCache(cacheKey); if (ellps) { return NN_NO_CHECK(ellps); } } auto res = d->runWithCodeParam( "SELECT ellipsoid.name, ellipsoid.semi_major_axis, " "ellipsoid.uom_auth_name, ellipsoid.uom_code, " "ellipsoid.inv_flattening, ellipsoid.semi_minor_axis, " "celestial_body.name AS body_name, ellipsoid.deprecated FROM " "ellipsoid JOIN celestial_body " "ON ellipsoid.celestial_body_auth_name = celestial_body.auth_name AND " "ellipsoid.celestial_body_code = celestial_body.code WHERE " "ellipsoid.auth_name = ? AND ellipsoid.code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("ellipsoid not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &semi_major_axis_str = row[1]; double semi_major_axis = c_locale_stod(semi_major_axis_str); const auto &uom_auth_name = row[2]; const auto &uom_code = row[3]; const auto &inv_flattening_str = row[4]; const auto &semi_minor_axis_str = row[5]; const auto &body = row[6]; const bool deprecated = row[7] == "1"; auto uom = d->createUnitOfMeasure(uom_auth_name, uom_code); auto props = d->createProperties(code, name, deprecated, nullptr); if (!inv_flattening_str.empty()) { auto ellps = datum::Ellipsoid::createFlattenedSphere( props, common::Length(semi_major_axis, uom), common::Scale(c_locale_stod(inv_flattening_str)), body); d->context()->d->cache(cacheKey, ellps); return ellps; } else if (semi_major_axis_str == semi_minor_axis_str) { auto ellps = datum::Ellipsoid::createSphere( props, common::Length(semi_major_axis, uom), body); d->context()->d->cache(cacheKey, ellps); return ellps; } else { auto ellps = datum::Ellipsoid::createTwoAxis( props, common::Length(semi_major_axis, uom), common::Length(c_locale_stod(semi_minor_axis_str), uom), body); d->context()->d->cache(cacheKey, ellps); return ellps; } } catch (const std::exception &ex) { throw buildFactoryException("ellipsoid", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a datum::GeodeticReferenceFrame from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ datum::GeodeticReferenceFrameNNPtr AuthorityFactory::createGeodeticDatum(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto datum = d->context()->d->getGeodeticDatumFromCache(cacheKey); if (datum) { return NN_NO_CHECK(datum); } } auto res = d->runWithCodeParam( "SELECT name, ellipsoid_auth_name, ellipsoid_code, " "prime_meridian_auth_name, prime_meridian_code, area_of_use_auth_name, " "area_of_use_code, publication_date, deprecated FROM geodetic_datum " "WHERE " "auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("geodetic datum not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &ellipsoid_auth_name = row[1]; const auto &ellipsoid_code = row[2]; const auto &prime_meridian_auth_name = row[3]; const auto &prime_meridian_code = row[4]; const auto &area_of_use_auth_name = row[5]; const auto &area_of_use_code = row[6]; const auto &publication_date = row[7]; const bool deprecated = row[8] == "1"; auto ellipsoid = d->createFactory(ellipsoid_auth_name) ->createEllipsoid(ellipsoid_code); auto pm = d->createFactory(prime_meridian_auth_name) ->createPrimeMeridian(prime_meridian_code); auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); auto anchor = util::optional(); if (!publication_date.empty()) { props.set("PUBLICATION_DATE", publication_date); } auto datum = datum::GeodeticReferenceFrame::create(props, ellipsoid, anchor, pm); d->context()->d->cache(cacheKey, datum); return datum; } catch (const std::exception &ex) { throw buildFactoryException("geodetic reference frame", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a datum::VerticalReferenceFrame from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ datum::VerticalReferenceFrameNNPtr AuthorityFactory::createVerticalDatum(const std::string &code) const { auto res = d->runWithCodeParam( "SELECT name, area_of_use_auth_name, area_of_use_code, deprecated FROM " "vertical_datum WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("vertical datum not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &area_of_use_auth_name = row[1]; const auto &area_of_use_code = row[2]; const bool deprecated = row[3] == "1"; auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); auto anchor = util::optional(); return datum::VerticalReferenceFrame::create(props, anchor); } catch (const std::exception &ex) { throw buildFactoryException("vertical reference frame", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a datum::Datum from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ datum::DatumNNPtr AuthorityFactory::createDatum(const std::string &code) const { auto res = d->run("SELECT 'geodetic_datum' FROM geodetic_datum WHERE " "auth_name = ? AND code = ? " "UNION ALL SELECT 'vertical_datum' FROM vertical_datum WHERE " "auth_name = ? AND code = ?", {d->authority(), code, d->authority(), code}); if (res.empty()) { throw NoSuchAuthorityCodeException("datum not found", d->authority(), code); } if (res.front()[0] == "geodetic_datum") { return createGeodeticDatum(code); } return createVerticalDatum(code); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static cs::MeridianPtr createMeridian(const std::string &val) { try { const std::string degW(std::string("\xC2\xB0") + "W"); if (ends_with(val, degW)) { return cs::Meridian::create(common::Angle( -c_locale_stod(val.substr(0, val.size() - degW.size())))); } const std::string degE(std::string("\xC2\xB0") + "E"); if (ends_with(val, degE)) { return cs::Meridian::create(common::Angle( c_locale_stod(val.substr(0, val.size() - degE.size())))); } } catch (const std::exception &) { } return nullptr; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns a cs::CoordinateSystem from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ cs::CoordinateSystemNNPtr AuthorityFactory::createCoordinateSystem(const std::string &code) const { const auto cacheKey(d->authority() + code); { auto cs = d->context()->d->getCoordinateSystemFromCache(cacheKey); if (cs) { return NN_NO_CHECK(cs); } } auto res = d->runWithCodeParam( "SELECT axis.name, abbrev, orientation, uom_auth_name, uom_code, " "cs.type FROM " "axis LEFT JOIN coordinate_system cs ON " "axis.coordinate_system_auth_name = cs.auth_name AND " "axis.coordinate_system_code = cs.code WHERE " "coordinate_system_auth_name = ? AND coordinate_system_code = ? ORDER " "BY coordinate_system_order", code); if (res.empty()) { throw NoSuchAuthorityCodeException("coordinate system not found", d->authority(), code); } const auto &csType = res.front()[5]; std::vector axisList; for (const auto &row : res) { const auto &name = row[0]; const auto &abbrev = row[1]; const auto &orientation = row[2]; const auto &uom_auth_name = row[3]; const auto &uom_code = row[4]; auto uom = d->createUnitOfMeasure(uom_auth_name, uom_code); auto props = util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, name); const cs::AxisDirection *direction = cs::AxisDirection::valueOf(orientation); cs::MeridianPtr meridian; if (direction == nullptr) { if (orientation == "Geocentre > equator/0" "\xC2\xB0" "E") { direction = &(cs::AxisDirection::GEOCENTRIC_X); } else if (orientation == "Geocentre > equator/90" "\xC2\xB0" "E") { direction = &(cs::AxisDirection::GEOCENTRIC_Y); } else if (orientation == "Geocentre > north pole") { direction = &(cs::AxisDirection::GEOCENTRIC_Z); } else if (starts_with(orientation, "North along ")) { direction = &(cs::AxisDirection::NORTH); meridian = createMeridian(orientation.substr(strlen("North along "))); } else if (starts_with(orientation, "South along ")) { direction = &(cs::AxisDirection::SOUTH); meridian = createMeridian(orientation.substr(strlen("South along "))); } else { throw FactoryException("unknown axis direction: " + orientation); } } axisList.emplace_back(cs::CoordinateSystemAxis::create( props, abbrev, *direction, uom, meridian)); } const auto cacheAndRet = [this, &cacheKey](const cs::CoordinateSystemNNPtr &cs) { d->context()->d->cache(cacheKey, cs); return cs; }; auto props = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, d->authority()) .set(metadata::Identifier::CODE_KEY, code); if (csType == "ellipsoidal") { if (axisList.size() == 2) { return cacheAndRet( cs::EllipsoidalCS::create(props, axisList[0], axisList[1])); } if (axisList.size() == 3) { return cacheAndRet(cs::EllipsoidalCS::create( props, axisList[0], axisList[1], axisList[2])); } throw FactoryException("invalid number of axis for EllipsoidalCS"); } if (csType == "Cartesian") { if (axisList.size() == 2) { return cacheAndRet( cs::CartesianCS::create(props, axisList[0], axisList[1])); } if (axisList.size() == 3) { return cacheAndRet(cs::CartesianCS::create( props, axisList[0], axisList[1], axisList[2])); } throw FactoryException("invalid number of axis for CartesianCS"); } if (csType == "vertical") { if (axisList.size() == 1) { return cacheAndRet(cs::VerticalCS::create(props, axisList[0])); } throw FactoryException("invalid number of axis for VerticalCS"); } throw FactoryException("unhandled coordinate system type: " + csType); } // --------------------------------------------------------------------------- /** \brief Returns a crs::GeodeticCRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::GeodeticCRSNNPtr AuthorityFactory::createGeodeticCRS(const std::string &code) const { return createGeodeticCRS(code, false); } // --------------------------------------------------------------------------- /** \brief Returns a crs::GeographicCRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::GeographicCRSNNPtr AuthorityFactory::createGeographicCRS(const std::string &code) const { return NN_NO_CHECK(util::nn_dynamic_pointer_cast( createGeodeticCRS(code, true))); } // --------------------------------------------------------------------------- static crs::GeodeticCRSNNPtr cloneWithProps(const crs::GeodeticCRSNNPtr &geodCRS, const util::PropertyMap &props) { auto cs = geodCRS->coordinateSystem(); auto datum = geodCRS->datum(); if (!datum) { return geodCRS; } auto ellipsoidalCS = util::nn_dynamic_pointer_cast(cs); if (ellipsoidalCS) { return crs::GeographicCRS::create(props, NN_NO_CHECK(datum), NN_NO_CHECK(ellipsoidalCS)); } auto geocentricCS = util::nn_dynamic_pointer_cast(cs); if (geocentricCS) { return crs::GeodeticCRS::create(props, NN_NO_CHECK(datum), NN_NO_CHECK(geocentricCS)); } return geodCRS; } // --------------------------------------------------------------------------- crs::GeodeticCRSNNPtr AuthorityFactory::createGeodeticCRS(const std::string &code, bool geographicOnly) const { const auto cacheKey(d->authority() + code); auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { auto geogCRS = std::dynamic_pointer_cast(crs); if (geogCRS) { return NN_NO_CHECK(geogCRS); } throw NoSuchAuthorityCodeException("geodeticCRS not found", d->authority(), code); } std::string sql("SELECT name, type, coordinate_system_auth_name, " "coordinate_system_code, datum_auth_name, datum_code, " "area_of_use_auth_name, area_of_use_code, text_definition, " "deprecated FROM " "geodetic_crs WHERE auth_name = ? AND code = ?"); if (geographicOnly) { sql += " AND type in (" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED ")"; } auto res = d->runWithCodeParam(sql, code); if (res.empty()) { throw NoSuchAuthorityCodeException("geodeticCRS not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &type = row[1]; const auto &cs_auth_name = row[2]; const auto &cs_code = row[3]; const auto &datum_auth_name = row[4]; const auto &datum_code = row[5]; const auto &area_of_use_auth_name = row[6]; const auto &area_of_use_code = row[7]; const auto &text_definition = row[8]; const bool deprecated = row[9] == "1"; auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); if (!text_definition.empty()) { DatabaseContext::Private::RecursionDetector detector(d->context()); auto obj = createFromUserInput( pj_add_type_crs_if_needed(text_definition), d->context()); auto geodCRS = util::nn_dynamic_pointer_cast(obj); if (geodCRS) { auto crsRet = cloneWithProps(NN_NO_CHECK(geodCRS), props); d->context()->d->cache(cacheKey, crsRet); return crsRet; } auto boundCRS = dynamic_cast(obj.get()); if (boundCRS) { geodCRS = util::nn_dynamic_pointer_cast( boundCRS->baseCRS()); if (geodCRS) { auto newBoundCRS = crs::BoundCRS::create( cloneWithProps(NN_NO_CHECK(geodCRS), props), boundCRS->hubCRS(), boundCRS->transformation()); return NN_NO_CHECK( util::nn_dynamic_pointer_cast( newBoundCRS->baseCRSWithCanonicalBoundCRS())); } } throw FactoryException( "text_definition does not define a GeodeticCRS"); } auto cs = d->createFactory(cs_auth_name)->createCoordinateSystem(cs_code); auto datum = d->createFactory(datum_auth_name)->createGeodeticDatum(datum_code); auto ellipsoidalCS = util::nn_dynamic_pointer_cast(cs); if ((type == GEOG_2D || type == GEOG_3D) && ellipsoidalCS) { auto crsRet = crs::GeographicCRS::create( props, datum, NN_NO_CHECK(ellipsoidalCS)); d->context()->d->cache(cacheKey, crsRet); return crsRet; } auto geocentricCS = util::nn_dynamic_pointer_cast(cs); if (type == GEOCENTRIC && geocentricCS) { auto crsRet = crs::GeodeticCRS::create(props, datum, NN_NO_CHECK(geocentricCS)); d->context()->d->cache(cacheKey, crsRet); return crsRet; } throw FactoryException("unsupported (type, CS type) for geodeticCRS: " + type + ", " + cs->getWKT2Type(true)); } catch (const std::exception &ex) { throw buildFactoryException("geodeticCRS", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a crs::VerticalCRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::VerticalCRSNNPtr AuthorityFactory::createVerticalCRS(const std::string &code) const { const auto cacheKey(d->authority() + code); auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { auto projCRS = std::dynamic_pointer_cast(crs); if (projCRS) { return NN_NO_CHECK(projCRS); } throw NoSuchAuthorityCodeException("verticalCRS not found", d->authority(), code); } auto res = d->runWithCodeParam( "SELECT name, coordinate_system_auth_name, " "coordinate_system_code, datum_auth_name, datum_code, " "area_of_use_auth_name, area_of_use_code, deprecated FROM " "vertical_crs WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("verticalCRS not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &cs_auth_name = row[1]; const auto &cs_code = row[2]; const auto &datum_auth_name = row[3]; const auto &datum_code = row[4]; const auto &area_of_use_auth_name = row[5]; const auto &area_of_use_code = row[6]; const bool deprecated = row[7] == "1"; auto cs = d->createFactory(cs_auth_name)->createCoordinateSystem(cs_code); auto datum = d->createFactory(datum_auth_name)->createVerticalDatum(datum_code); auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); auto verticalCS = util::nn_dynamic_pointer_cast(cs); if (verticalCS) { auto crsRet = crs::VerticalCRS::create(props, datum, NN_NO_CHECK(verticalCS)); d->context()->d->cache(cacheKey, crsRet); return crsRet; } throw FactoryException("unsupported CS type for verticalCRS: " + cs->getWKT2Type(true)); } catch (const std::exception &ex) { throw buildFactoryException("verticalCRS", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a operation::Conversion from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ operation::ConversionNNPtr AuthorityFactory::createConversion(const std::string &code) const { static const char *sql = "SELECT name, area_of_use_auth_name, area_of_use_code, " "method_auth_name, method_code, method_name, " "param1_auth_name, param1_code, param1_name, param1_value, " "param1_uom_auth_name, param1_uom_code, " "param2_auth_name, param2_code, param2_name, param2_value, " "param2_uom_auth_name, param2_uom_code, " "param3_auth_name, param3_code, param3_name, param3_value, " "param3_uom_auth_name, param3_uom_code, " "param4_auth_name, param4_code, param4_name, param4_value, " "param4_uom_auth_name, param4_uom_code, " "param5_auth_name, param5_code, param5_name, param5_value, " "param5_uom_auth_name, param5_uom_code, " "param6_auth_name, param6_code, param6_name, param6_value, " "param6_uom_auth_name, param6_uom_code, " "param7_auth_name, param7_code, param7_name, param7_value, " "param7_uom_auth_name, param7_uom_code, " "deprecated FROM conversion WHERE auth_name = ? AND code = ?"; auto res = d->runWithCodeParam(sql, code); if (res.empty()) { try { // Conversions using methods Change of Vertical Unit or // Height Depth Reversal are stored in other_transformation auto op = createCoordinateOperation( code, false /* allowConcatenated */, false /* usePROJAlternativeGridNames */, "other_transformation"); auto conv = util::nn_dynamic_pointer_cast(op); if (conv) { return NN_NO_CHECK(conv); } } catch (const std::exception &) { } throw NoSuchAuthorityCodeException("conversion not found", d->authority(), code); } try { const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; const auto &area_of_use_code = row[idx++]; const auto &method_auth_name = row[idx++]; const auto &method_code = row[idx++]; const auto &method_name = row[idx++]; const size_t base_param_idx = idx; std::vector parameters; std::vector values; constexpr int N_MAX_PARAMS = 7; for (int i = 0; i < N_MAX_PARAMS; ++i) { const auto ¶m_auth_name = row[base_param_idx + i * 6 + 0]; if (param_auth_name.empty()) { break; } const auto ¶m_code = row[base_param_idx + i * 6 + 1]; const auto ¶m_name = row[base_param_idx + i * 6 + 2]; const auto ¶m_value = row[base_param_idx + i * 6 + 3]; const auto ¶m_uom_auth_name = row[base_param_idx + i * 6 + 4]; const auto ¶m_uom_code = row[base_param_idx + i * 6 + 5]; parameters.emplace_back(operation::OperationParameter::create( util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, param_auth_name) .set(metadata::Identifier::CODE_KEY, param_code) .set(common::IdentifiedObject::NAME_KEY, param_name))); std::string normalized_uom_code(param_uom_code); const double normalized_value = normalizeMeasure( param_uom_code, param_value, normalized_uom_code); auto uom = d->createUnitOfMeasure(param_uom_auth_name, normalized_uom_code); values.emplace_back(operation::ParameterValue::create( common::Measure(normalized_value, uom))); } const bool deprecated = row[base_param_idx + N_MAX_PARAMS * 6] == "1"; auto propConversion = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); auto propMethod = util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, method_name); if (!method_auth_name.empty()) { propMethod .set(metadata::Identifier::CODESPACE_KEY, method_auth_name) .set(metadata::Identifier::CODE_KEY, method_code); } return operation::Conversion::create(propConversion, propMethod, parameters, values); } catch (const std::exception &ex) { throw buildFactoryException("conversion", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a crs::ProjectedCRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::ProjectedCRSNNPtr AuthorityFactory::createProjectedCRS(const std::string &code) const { const auto cacheKey(d->authority() + code); auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { auto projCRS = std::dynamic_pointer_cast(crs); if (projCRS) { return NN_NO_CHECK(projCRS); } throw NoSuchAuthorityCodeException("projectedCRS not found", d->authority(), code); } auto res = d->runWithCodeParam( "SELECT name, coordinate_system_auth_name, " "coordinate_system_code, geodetic_crs_auth_name, geodetic_crs_code, " "conversion_auth_name, conversion_code, " "area_of_use_auth_name, area_of_use_code, text_definition, " "deprecated FROM projected_crs WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("projectedCRS not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &cs_auth_name = row[1]; const auto &cs_code = row[2]; const auto &geodetic_crs_auth_name = row[3]; const auto &geodetic_crs_code = row[4]; const auto &conversion_auth_name = row[5]; const auto &conversion_code = row[6]; const auto &area_of_use_auth_name = row[7]; const auto &area_of_use_code = row[8]; const auto &text_definition = row[9]; const bool deprecated = row[10] == "1"; auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); if (!text_definition.empty()) { DatabaseContext::Private::RecursionDetector detector(d->context()); auto obj = createFromUserInput( pj_add_type_crs_if_needed(text_definition), d->context()); auto projCRS = dynamic_cast(obj.get()); if (projCRS) { const auto conv = projCRS->derivingConversion(); auto newConv = (conv->nameStr() == "unnamed") ? operation::Conversion::create( util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, name), conv->method(), conv->parameterValues()) : conv; auto crsRet = crs::ProjectedCRS::create( props, projCRS->baseCRS(), newConv, projCRS->coordinateSystem()); d->context()->d->cache(cacheKey, crsRet); return crsRet; } auto boundCRS = dynamic_cast(obj.get()); if (boundCRS) { projCRS = dynamic_cast( boundCRS->baseCRS().get()); if (projCRS) { auto newBoundCRS = crs::BoundCRS::create( crs::ProjectedCRS::create(props, projCRS->baseCRS(), projCRS->derivingConversion(), projCRS->coordinateSystem()), boundCRS->hubCRS(), boundCRS->transformation()); return NN_NO_CHECK( util::nn_dynamic_pointer_cast( newBoundCRS->baseCRSWithCanonicalBoundCRS())); } } throw FactoryException( "text_definition does not define a ProjectedCRS"); } auto cs = d->createFactory(cs_auth_name)->createCoordinateSystem(cs_code); auto baseCRS = d->createFactory(geodetic_crs_auth_name) ->createGeodeticCRS(geodetic_crs_code); auto conv = d->createFactory(conversion_auth_name) ->createConversion(conversion_code); if (conv->nameStr() == "unnamed") { conv = conv->shallowClone(); conv->setProperties(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, name)); } auto cartesianCS = util::nn_dynamic_pointer_cast(cs); if (cartesianCS) { auto crsRet = crs::ProjectedCRS::create(props, baseCRS, conv, NN_NO_CHECK(cartesianCS)); d->context()->d->cache(cacheKey, crsRet); return crsRet; } throw FactoryException("unsupported CS type for projectedCRS: " + cs->getWKT2Type(true)); } catch (const std::exception &ex) { throw buildFactoryException("projectedCRS", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a crs::CompoundCRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::CompoundCRSNNPtr AuthorityFactory::createCompoundCRS(const std::string &code) const { auto res = d->runWithCodeParam( "SELECT name, horiz_crs_auth_name, horiz_crs_code, " "vertical_crs_auth_name, vertical_crs_code, " "area_of_use_auth_name, area_of_use_code, deprecated FROM " "compound_crs WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("compoundCRS not found", d->authority(), code); } try { const auto &row = res.front(); const auto &name = row[0]; const auto &horiz_crs_auth_name = row[1]; const auto &horiz_crs_code = row[2]; const auto &vertical_crs_auth_name = row[3]; const auto &vertical_crs_code = row[4]; const auto &area_of_use_auth_name = row[5]; const auto &area_of_use_code = row[6]; const bool deprecated = row[7] == "1"; auto horizCRS = d->createFactory(horiz_crs_auth_name) ->createCoordinateReferenceSystem(horiz_crs_code, false); auto vertCRS = d->createFactory(vertical_crs_auth_name) ->createVerticalCRS(vertical_crs_code); auto props = d->createProperties( code, name, deprecated, area_of_use_auth_name, area_of_use_code); return crs::CompoundCRS::create( props, std::vector{horizCRS, vertCRS}); } catch (const std::exception &ex) { throw buildFactoryException("compoundCRS", code, ex); } } // --------------------------------------------------------------------------- /** \brief Returns a crs::CRS from the specified code. * * @param code Object code allocated by authority. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem( const std::string &code) const { return createCoordinateReferenceSystem(code, true); } //! @cond Doxygen_Suppress crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, bool allowCompound) const { const auto cacheKey(d->authority() + code); auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { return NN_NO_CHECK(crs); } auto res = d->runWithCodeParam( "SELECT type FROM crs_view WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("crs not found", d->authority(), code); } const auto &type = res.front()[0]; if (type == GEOG_2D || type == GEOG_3D || type == GEOCENTRIC) { return createGeodeticCRS(code); } if (type == VERTICAL) { return createVerticalCRS(code); } if (type == PROJECTED) { return createProjectedCRS(code); } if (allowCompound && type == COMPOUND) { return createCompoundCRS(code); } throw FactoryException("unhandled CRS type: " + type); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static util::PropertyMap createMapNameEPSGCode(const std::string &name, int code) { return util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, name) .set(metadata::Identifier::CODESPACE_KEY, metadata::Identifier::EPSG) .set(metadata::Identifier::CODE_KEY, code); } // --------------------------------------------------------------------------- static operation::OperationParameterNNPtr createOpParamNameEPSGCode(int code) { const char *name = operation::OperationParameter::getNameForEPSGCode(code); assert(name); return operation::OperationParameter::create( createMapNameEPSGCode(name, code)); } static operation::ParameterValueNNPtr createLength(const std::string &value, const UnitOfMeasure &uom) { return operation::ParameterValue::create( common::Length(c_locale_stod(value), uom)); } static operation::ParameterValueNNPtr createAngle(const std::string &value, const UnitOfMeasure &uom) { return operation::ParameterValue::create( common::Angle(c_locale_stod(value), uom)); } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns a operation::CoordinateOperation from the specified code. * * @param code Object code allocated by authority. * @param usePROJAlternativeGridNames Whether PROJ alternative grid names * should be substituted to the official grid names. * @return object. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( const std::string &code, bool usePROJAlternativeGridNames) const { return createCoordinateOperation(code, true, usePROJAlternativeGridNames, std::string()); } operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( const std::string &code, bool allowConcatenated, bool usePROJAlternativeGridNames, const std::string &typeIn) const { std::string type(typeIn); if (type.empty()) { auto res = d->runWithCodeParam( "SELECT type FROM coordinate_operation_with_conversion_view " "WHERE auth_name = ? AND code = ?", code); if (res.empty()) { throw NoSuchAuthorityCodeException("coordinate operation not found", d->authority(), code); } type = res.front()[0]; } if (type == "conversion") { return createConversion(code); } if (type == "helmert_transformation") { auto res = d->runWithCodeParam( "SELECT name, description, scope, " "method_auth_name, method_code, method_name, " "source_crs_auth_name, source_crs_code, target_crs_auth_name, " "target_crs_code, area_of_use_auth_name, area_of_use_code, " "accuracy, tx, ty, tz, translation_uom_auth_name, " "translation_uom_code, rx, ry, rz, rotation_uom_auth_name, " "rotation_uom_code, scale_difference, " "scale_difference_uom_auth_name, scale_difference_uom_code, " "rate_tx, rate_ty, rate_tz, rate_translation_uom_auth_name, " "rate_translation_uom_code, rate_rx, rate_ry, rate_rz, " "rate_rotation_uom_auth_name, rate_rotation_uom_code, " "rate_scale_difference, rate_scale_difference_uom_auth_name, " "rate_scale_difference_uom_code, epoch, epoch_uom_auth_name, " "epoch_uom_code, px, py, pz, pivot_uom_auth_name, pivot_uom_code, " "operation_version, deprecated FROM " "helmert_transformation WHERE auth_name = ? AND code = ?", code); if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException( "helmert_transformation not found", d->authority(), code); } try { const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &description = row[idx++]; const auto &scope = row[idx++]; const auto &method_auth_name = row[idx++]; const auto &method_code = row[idx++]; const auto &method_name = row[idx++]; const auto &source_crs_auth_name = row[idx++]; const auto &source_crs_code = row[idx++]; const auto &target_crs_auth_name = row[idx++]; const auto &target_crs_code = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; const auto &area_of_use_code = row[idx++]; const auto &accuracy = row[idx++]; const auto &tx = row[idx++]; const auto &ty = row[idx++]; const auto &tz = row[idx++]; const auto &translation_uom_auth_name = row[idx++]; const auto &translation_uom_code = row[idx++]; const auto &rx = row[idx++]; const auto &ry = row[idx++]; const auto &rz = row[idx++]; const auto &rotation_uom_auth_name = row[idx++]; const auto &rotation_uom_code = row[idx++]; const auto &scale_difference = row[idx++]; const auto &scale_difference_uom_auth_name = row[idx++]; const auto &scale_difference_uom_code = row[idx++]; const auto &rate_tx = row[idx++]; const auto &rate_ty = row[idx++]; const auto &rate_tz = row[idx++]; const auto &rate_translation_uom_auth_name = row[idx++]; const auto &rate_translation_uom_code = row[idx++]; const auto &rate_rx = row[idx++]; const auto &rate_ry = row[idx++]; const auto &rate_rz = row[idx++]; const auto &rate_rotation_uom_auth_name = row[idx++]; const auto &rate_rotation_uom_code = row[idx++]; const auto &rate_scale_difference = row[idx++]; const auto &rate_scale_difference_uom_auth_name = row[idx++]; const auto &rate_scale_difference_uom_code = row[idx++]; const auto &epoch = row[idx++]; const auto &epoch_uom_auth_name = row[idx++]; const auto &epoch_uom_code = row[idx++]; const auto &px = row[idx++]; const auto &py = row[idx++]; const auto &pz = row[idx++]; const auto &pivot_uom_auth_name = row[idx++]; const auto &pivot_uom_code = row[idx++]; const auto &operation_version = row[idx++]; const auto &deprecated_str = row[idx++]; const bool deprecated = deprecated_str == "1"; assert(idx == row.size()); auto uom_translation = d->createUnitOfMeasure( translation_uom_auth_name, translation_uom_code); auto uom_epoch = epoch_uom_auth_name.empty() ? common::UnitOfMeasure::NONE : d->createUnitOfMeasure(epoch_uom_auth_name, epoch_uom_code); auto sourceCRS = d->createFactory(source_crs_auth_name) ->createCoordinateReferenceSystem(source_crs_code); auto targetCRS = d->createFactory(target_crs_auth_name) ->createCoordinateReferenceSystem(target_crs_code); std::vector parameters; std::vector values; parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION)); values.emplace_back(createLength(tx, uom_translation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION)); values.emplace_back(createLength(ty, uom_translation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION)); values.emplace_back(createLength(tz, uom_translation)); if (!rx.empty()) { // Helmert 7-, 8-, 10- or 15- parameter cases auto uom_rotation = d->createUnitOfMeasure( rotation_uom_auth_name, rotation_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_X_AXIS_ROTATION)); values.emplace_back(createAngle(rx, uom_rotation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_Y_AXIS_ROTATION)); values.emplace_back(createAngle(ry, uom_rotation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_Z_AXIS_ROTATION)); values.emplace_back(createAngle(rz, uom_rotation)); auto uom_scale_difference = scale_difference_uom_auth_name.empty() ? common::UnitOfMeasure::NONE : d->createUnitOfMeasure(scale_difference_uom_auth_name, scale_difference_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_SCALE_DIFFERENCE)); values.emplace_back(operation::ParameterValue::create( common::Scale(c_locale_stod(scale_difference), uom_scale_difference))); } if (!rate_tx.empty()) { // Helmert 15-parameter auto uom_rate_translation = d->createUnitOfMeasure( rate_translation_uom_auth_name, rate_translation_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_X_AXIS_TRANSLATION)); values.emplace_back( createLength(rate_tx, uom_rate_translation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Y_AXIS_TRANSLATION)); values.emplace_back( createLength(rate_ty, uom_rate_translation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Z_AXIS_TRANSLATION)); values.emplace_back( createLength(rate_tz, uom_rate_translation)); auto uom_rate_rotation = d->createUnitOfMeasure( rate_rotation_uom_auth_name, rate_rotation_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_X_AXIS_ROTATION)); values.emplace_back(createAngle(rate_rx, uom_rate_rotation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Y_AXIS_ROTATION)); values.emplace_back(createAngle(rate_ry, uom_rate_rotation)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_Z_AXIS_ROTATION)); values.emplace_back(createAngle(rate_rz, uom_rate_rotation)); auto uom_rate_scale_difference = d->createUnitOfMeasure(rate_scale_difference_uom_auth_name, rate_scale_difference_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_RATE_SCALE_DIFFERENCE)); values.emplace_back(operation::ParameterValue::create( common::Scale(c_locale_stod(rate_scale_difference), uom_rate_scale_difference))); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_REFERENCE_EPOCH)); values.emplace_back(operation::ParameterValue::create( common::Measure(c_locale_stod(epoch), uom_epoch))); } else if (uom_epoch != common::UnitOfMeasure::NONE) { // Helmert 8-parameter parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_TRANSFORMATION_REFERENCE_EPOCH)); values.emplace_back(operation::ParameterValue::create( common::Measure(c_locale_stod(epoch), uom_epoch))); } else if (!px.empty()) { // Molodensky-Badekas case auto uom_pivot = d->createUnitOfMeasure(pivot_uom_auth_name, pivot_uom_code); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_ORDINATE_1_EVAL_POINT)); values.emplace_back(createLength(px, uom_pivot)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_ORDINATE_2_EVAL_POINT)); values.emplace_back(createLength(py, uom_pivot)); parameters.emplace_back(createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_ORDINATE_3_EVAL_POINT)); values.emplace_back(createLength(pz, uom_pivot)); } auto props = d->createProperties(code, name, deprecated, description, scope, area_of_use_auth_name, area_of_use_code); if (!operation_version.empty()) { props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY, operation_version); } auto propsMethod = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, method_auth_name) .set(metadata::Identifier::CODE_KEY, method_code) .set(common::IdentifiedObject::NAME_KEY, method_name); std::vector accuracies; if (!accuracy.empty()) { accuracies.emplace_back( metadata::PositionalAccuracy::create(accuracy)); } return operation::Transformation::create( props, sourceCRS, targetCRS, nullptr, propsMethod, parameters, values, accuracies); } catch (const std::exception &ex) { throw buildFactoryException("transformation", code, ex); } } if (type == "grid_transformation") { auto res = d->runWithCodeParam( "SELECT name, description, scope, " "method_auth_name, method_code, method_name, " "source_crs_auth_name, source_crs_code, target_crs_auth_name, " "target_crs_code, area_of_use_auth_name, area_of_use_code, " "accuracy, grid_param_auth_name, grid_param_code, grid_param_name, " "grid_name, " "grid2_param_auth_name, grid2_param_code, grid2_param_name, " "grid2_name, " "interpolation_crs_auth_name, interpolation_crs_code, " "operation_version, deprecated FROM " "grid_transformation WHERE auth_name = ? AND code = ?", code); if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException("grid_transformation not found", d->authority(), code); } try { const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &description = row[idx++]; const auto &scope = row[idx++]; const auto &method_auth_name = row[idx++]; const auto &method_code = row[idx++]; const auto &method_name = row[idx++]; const auto &source_crs_auth_name = row[idx++]; const auto &source_crs_code = row[idx++]; const auto &target_crs_auth_name = row[idx++]; const auto &target_crs_code = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; const auto &area_of_use_code = row[idx++]; const auto &accuracy = row[idx++]; const auto &grid_param_auth_name = row[idx++]; const auto &grid_param_code = row[idx++]; const auto &grid_param_name = row[idx++]; const auto &grid_name = row[idx++]; const auto &grid2_param_auth_name = row[idx++]; const auto &grid2_param_code = row[idx++]; const auto &grid2_param_name = row[idx++]; const auto &grid2_name = row[idx++]; const auto &interpolation_crs_auth_name = row[idx++]; const auto &interpolation_crs_code = row[idx++]; const auto &operation_version = row[idx++]; const auto &deprecated_str = row[idx++]; const bool deprecated = deprecated_str == "1"; assert(idx == row.size()); auto sourceCRS = d->createFactory(source_crs_auth_name) ->createCoordinateReferenceSystem(source_crs_code); auto targetCRS = d->createFactory(target_crs_auth_name) ->createCoordinateReferenceSystem(target_crs_code); auto interpolationCRS = interpolation_crs_auth_name.empty() ? nullptr : d->createFactory(interpolation_crs_auth_name) ->createCoordinateReferenceSystem( interpolation_crs_code) .as_nullable(); std::vector parameters; std::vector values; parameters.emplace_back(operation::OperationParameter::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, grid_param_name) .set(metadata::Identifier::CODESPACE_KEY, grid_param_auth_name) .set(metadata::Identifier::CODE_KEY, grid_param_code))); values.emplace_back( operation::ParameterValue::createFilename(grid_name)); if (!grid2_name.empty()) { parameters.emplace_back(operation::OperationParameter::create( util::PropertyMap() .set(common::IdentifiedObject::NAME_KEY, grid2_param_name) .set(metadata::Identifier::CODESPACE_KEY, grid2_param_auth_name) .set(metadata::Identifier::CODE_KEY, grid2_param_code))); values.emplace_back( operation::ParameterValue::createFilename(grid2_name)); } auto props = d->createProperties(code, name, deprecated, description, scope, area_of_use_auth_name, area_of_use_code); if (!operation_version.empty()) { props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY, operation_version); } auto propsMethod = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, method_auth_name) .set(metadata::Identifier::CODE_KEY, method_code) .set(common::IdentifiedObject::NAME_KEY, method_name); std::vector accuracies; if (!accuracy.empty()) { accuracies.emplace_back( metadata::PositionalAccuracy::create(accuracy)); } auto transf = operation::Transformation::create( props, sourceCRS, targetCRS, interpolationCRS, propsMethod, parameters, values, accuracies); if (usePROJAlternativeGridNames) { return transf->substitutePROJAlternativeGridNames(d->context()); } return transf; } catch (const std::exception &ex) { throw buildFactoryException("transformation", code, ex); } } if (type == "other_transformation") { std::ostringstream buffer; buffer.imbue(std::locale::classic()); buffer << "SELECT name, description, scope, " "method_auth_name, method_code, method_name, " "source_crs_auth_name, source_crs_code, target_crs_auth_name, " "target_crs_code, area_of_use_auth_name, area_of_use_code, " "accuracy"; constexpr int N_MAX_PARAMS = 7; for (int i = 1; i <= N_MAX_PARAMS; ++i) { buffer << ", param" << i << "_auth_name"; buffer << ", param" << i << "_code"; buffer << ", param" << i << "_name"; buffer << ", param" << i << "_value"; buffer << ", param" << i << "_uom_auth_name"; buffer << ", param" << i << "_uom_code"; } buffer << ", operation_version, deprecated FROM other_transformation " "WHERE auth_name = ? AND code = ?"; auto res = d->runWithCodeParam(buffer.str(), code); if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException("other_transformation not found", d->authority(), code); } try { const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &description = row[idx++]; const auto &scope = row[idx++]; const auto &method_auth_name = row[idx++]; const auto &method_code = row[idx++]; const auto &method_name = row[idx++]; const auto &source_crs_auth_name = row[idx++]; const auto &source_crs_code = row[idx++]; const auto &target_crs_auth_name = row[idx++]; const auto &target_crs_code = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; const auto &area_of_use_code = row[idx++]; const auto &accuracy = row[idx++]; const size_t base_param_idx = idx; std::vector parameters; std::vector values; for (int i = 0; i < N_MAX_PARAMS; ++i) { const auto ¶m_auth_name = row[base_param_idx + i * 6 + 0]; if (param_auth_name.empty()) { break; } const auto ¶m_code = row[base_param_idx + i * 6 + 1]; const auto ¶m_name = row[base_param_idx + i * 6 + 2]; const auto ¶m_value = row[base_param_idx + i * 6 + 3]; const auto ¶m_uom_auth_name = row[base_param_idx + i * 6 + 4]; const auto ¶m_uom_code = row[base_param_idx + i * 6 + 5]; parameters.emplace_back(operation::OperationParameter::create( util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, param_auth_name) .set(metadata::Identifier::CODE_KEY, param_code) .set(common::IdentifiedObject::NAME_KEY, param_name))); std::string normalized_uom_code(param_uom_code); const double normalized_value = normalizeMeasure( param_uom_code, param_value, normalized_uom_code); auto uom = d->createUnitOfMeasure(param_uom_auth_name, normalized_uom_code); values.emplace_back(operation::ParameterValue::create( common::Measure(normalized_value, uom))); } idx = base_param_idx + 6 * N_MAX_PARAMS; const auto &operation_version = row[idx++]; const auto &deprecated_str = row[idx++]; const bool deprecated = deprecated_str == "1"; assert(idx == row.size()); auto sourceCRS = d->createFactory(source_crs_auth_name) ->createCoordinateReferenceSystem(source_crs_code); auto targetCRS = d->createFactory(target_crs_auth_name) ->createCoordinateReferenceSystem(target_crs_code); auto props = d->createProperties(code, name, deprecated, description, scope, area_of_use_auth_name, area_of_use_code); if (!operation_version.empty()) { props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY, operation_version); } std::vector accuracies; if (!accuracy.empty()) { accuracies.emplace_back( metadata::PositionalAccuracy::create(accuracy)); } if (method_auth_name == "PROJ") { if (method_code == "PROJString") { return operation::SingleOperation::createPROJBased( props, method_name, sourceCRS, targetCRS, accuracies); } else if (method_code == "WKT") { auto op = util::nn_dynamic_pointer_cast< operation::CoordinateOperation>( WKTParser().createFromWKT(method_name)); if (!op) { throw FactoryException("WKT string does not express a " "coordinate operation"); } op->setCRSs(sourceCRS, targetCRS, nullptr); return NN_NO_CHECK(op); } } auto propsMethod = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, method_auth_name) .set(metadata::Identifier::CODE_KEY, method_code) .set(common::IdentifiedObject::NAME_KEY, method_name); if (method_auth_name == metadata::Identifier::EPSG) { int method_code_int = std::atoi(method_code.c_str()); if (operation::isAxisOrderReversal(method_code_int) || method_code_int == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT || method_code_int == EPSG_CODE_METHOD_HEIGHT_DEPTH_REVERSAL) { auto op = operation::Conversion::create(props, propsMethod, parameters, values); op->setCRSs(sourceCRS, targetCRS, nullptr); return op; } } return operation::Transformation::create( props, sourceCRS, targetCRS, nullptr, propsMethod, parameters, values, accuracies); } catch (const std::exception &ex) { throw buildFactoryException("transformation", code, ex); } } if (allowConcatenated && type == "concatenated_operation") { auto res = d->runWithCodeParam( "SELECT name, description, scope, " "source_crs_auth_name, source_crs_code, " "target_crs_auth_name, target_crs_code, " "area_of_use_auth_name, area_of_use_code, accuracy, " "operation_version, deprecated FROM " "concatenated_operation WHERE auth_name = ? AND code = ?", code); if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException( "concatenated_operation not found", d->authority(), code); } auto resSteps = d->runWithCodeParam( "SELECT step_auth_name, step_code FROM " "concatenated_operation_step WHERE operation_auth_name = ? " "AND operation_code = ? ORDER BY step_number", code); try { const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &description = row[idx++]; const auto &scope = row[idx++]; const auto &source_crs_auth_name = row[idx++]; const auto &source_crs_code = row[idx++]; const auto &target_crs_auth_name = row[idx++]; const auto &target_crs_code = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; const auto &area_of_use_code = row[idx++]; const auto &accuracy = row[idx++]; const auto &operation_version = row[idx++]; const auto &deprecated_str = row[idx++]; const bool deprecated = deprecated_str == "1"; std::vector operations; for (const auto &rowStep : resSteps) { const auto &step_auth_name = rowStep[0]; const auto &step_code = rowStep[1]; operations.push_back( d->createFactory(step_auth_name) ->createCoordinateOperation(step_code, false, usePROJAlternativeGridNames, std::string())); } operation::ConcatenatedOperation::fixStepsDirection( d->createFactory(source_crs_auth_name) ->createCoordinateReferenceSystem(source_crs_code), d->createFactory(target_crs_auth_name) ->createCoordinateReferenceSystem(target_crs_code), operations); auto props = d->createProperties(code, name, deprecated, description, scope, area_of_use_auth_name, area_of_use_code); if (!operation_version.empty()) { props.set(operation::CoordinateOperation::OPERATION_VERSION_KEY, operation_version); } std::vector accuracies; if (!accuracy.empty()) { accuracies.emplace_back( metadata::PositionalAccuracy::create(accuracy)); } else { // Try to compute a reasonable accuracy from the members double totalAcc = -1; try { for (const auto &op : operations) { auto accs = op->coordinateOperationAccuracies(); if (accs.size() == 1) { double acc = c_locale_stod(accs[0]->value()); if (totalAcc < 0) { totalAcc = acc; } else { totalAcc += acc; } } else { totalAcc = -1; break; } } if (totalAcc >= 0) { accuracies.emplace_back( metadata::PositionalAccuracy::create( toString(totalAcc))); } } catch (const std::exception &) { } } return operation::ConcatenatedOperation::create(props, operations, accuracies); } catch (const std::exception &ex) { throw buildFactoryException("transformation", code, ex); } } throw FactoryException("unhandled coordinate operation type: " + type); } // --------------------------------------------------------------------------- /** \brief Returns a list operation::CoordinateOperation between two CRS. * * The list is ordered with preferred operations first. No attempt is made * at inferring operations that are not explicitly in the database. * * Deprecated operations are rejected. * * @param sourceCRSCode Source CRS code allocated by authority. * @param targetCRSCode Source CRS code allocated by authority. * @return list of coordinate operations * @throw NoSuchAuthorityCodeException * @throw FactoryException */ std::vector AuthorityFactory::createFromCoordinateReferenceSystemCodes( const std::string &sourceCRSCode, const std::string &targetCRSCode) const { return createFromCoordinateReferenceSystemCodes( d->authority(), sourceCRSCode, d->authority(), targetCRSCode, false, false, false); } // --------------------------------------------------------------------------- /** \brief Returns a list operation::CoordinateOperation between two CRS. * * The list is ordered with preferred operations first. No attempt is made * at inferring operations that are not explicitly in the database (see * createFromCRSCodesWithIntermediates() for that), and only * source -> target operations are searched (ie if target -> source is present, * you need to call this method with the arguments reversed, and apply the * reverse transformations). * * Deprecated operations are rejected. * * If getAuthority() returns empty, then coordinate operations from all * authorities are considered. * * @param sourceCRSAuthName Authority name of sourceCRSCode * @param sourceCRSCode Source CRS code allocated by authority * sourceCRSAuthName. * @param targetCRSAuthName Authority name of targetCRSCode * @param targetCRSCode Source CRS code allocated by authority * targetCRSAuthName. * @param usePROJAlternativeGridNames Whether PROJ alternative grid names * should be substituted to the official grid names. * @param discardIfMissingGrid Whether coordinate operations that reference * missing grids should be removed from the result set. * @param discardSuperseded Whether cordinate operations that are superseded * (but not deprecated) should be removed from the result set. * @param tryReverseOrder whether to search in the reverse order too (and thus * inverse results found that way) * @param reportOnlyIntersectingTransformations if intersectingExtent1 and * intersectingExtent2 should be honored in a strict way. * @param intersectingExtent1 Optional extent that the resulting operations * must intersect. * @param intersectingExtent2 Optional extent that the resulting operations * must intersect. * @return list of coordinate operations * @throw NoSuchAuthorityCodeException * @throw FactoryException */ std::vector AuthorityFactory::createFromCoordinateReferenceSystemCodes( const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded, bool tryReverseOrder, bool reportOnlyIntersectingTransformations, const metadata::ExtentPtr &intersectingExtent1, const metadata::ExtentPtr &intersectingExtent2) const { auto cacheKey(d->authority()); cacheKey += sourceCRSAuthName.empty() ? "{empty}" : sourceCRSAuthName; cacheKey += sourceCRSCode; cacheKey += targetCRSAuthName.empty() ? "{empty}" : targetCRSAuthName; cacheKey += targetCRSCode; cacheKey += (usePROJAlternativeGridNames ? '1' : '0'); cacheKey += (discardIfMissingGrid ? '1' : '0'); cacheKey += (discardSuperseded ? '1' : '0'); cacheKey += (tryReverseOrder ? '1' : '0'); cacheKey += (reportOnlyIntersectingTransformations ? '1' : '0'); for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { if (extent) { const auto &geogExtent = extent->geographicElements(); if (geogExtent.size() == 1) { auto bbox = dynamic_cast( geogExtent[0].get()); if (bbox) { cacheKey += toString(bbox->southBoundLatitude()); cacheKey += toString(bbox->westBoundLongitude()); cacheKey += toString(bbox->northBoundLatitude()); cacheKey += toString(bbox->eastBoundLongitude()); } } } } std::vector list; if (d->context()->d->getCRSToCRSCoordOpFromCache(cacheKey, list)) { return list; } if (!targetCRSAuthName.empty()) { auto targetFactory = d->createFactory(targetCRSAuthName); try { auto targetCRS = targetFactory->createProjectedCRS(targetCRSCode); const auto &baseIds = targetCRS->baseCRS()->identifiers(); if (sourceCRSAuthName.empty() || (!baseIds.empty() && *(baseIds.front()->codeSpace()) == sourceCRSAuthName && baseIds.front()->code() == sourceCRSCode)) { bool ok = true; auto conv = targetCRS->derivingConversion(); if (d->hasAuthorityRestriction()) { ok = *(conv->identifiers().front()->codeSpace()) == d->authority(); } if (ok) { list.emplace_back(conv); d->context()->d->cache(cacheKey, list); return list; } } } catch (const std::exception &) { } } std::string sql; if (discardSuperseded) { sql = "SELECT source_crs_auth_name, source_crs_code, " "target_crs_auth_name, target_crs_code, " "cov.auth_name, cov.code, cov.table_name, " "area.south_lat, area.west_lon, area.north_lat, area.east_lon, " "ss.replacement_auth_name, ss.replacement_code FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " "cov.area_of_use_code = area.code " "LEFT JOIN supersession ss ON " "ss.superseded_table_name = cov.table_name AND " "ss.superseded_auth_name = cov.auth_name AND " "ss.superseded_code = cov.code AND " "ss.superseded_table_name = ss.replacement_table_name " "WHERE "; } else { sql = "SELECT source_crs_auth_name, source_crs_code, " "target_crs_auth_name, target_crs_code, " "cov.auth_name, cov.code, cov.table_name, " "area.south_lat, area.west_lon, area.north_lat, area.east_lon " "FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " "cov.area_of_use_code = area.code " "WHERE "; } ListOfParams params; if (!sourceCRSAuthName.empty() && !targetCRSAuthName.empty()) { if (tryReverseOrder) { sql += "((source_crs_auth_name = ? AND source_crs_code = ? AND " "target_crs_auth_name = ? AND target_crs_code = ?) OR " "(source_crs_auth_name = ? AND source_crs_code = ? AND " "target_crs_auth_name = ? AND target_crs_code = ?)) AND "; params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); } else { sql += "source_crs_auth_name = ? AND source_crs_code = ? AND " "target_crs_auth_name = ? AND target_crs_code = ? AND "; params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); } } else if (!sourceCRSAuthName.empty()) { if (tryReverseOrder) { sql += "((source_crs_auth_name = ? AND source_crs_code = ?) OR " "(target_crs_auth_name = ? AND target_crs_code = ?)) AND "; params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); } else { sql += "source_crs_auth_name = ? AND source_crs_code = ? AND "; params.emplace_back(sourceCRSAuthName); params.emplace_back(sourceCRSCode); } } else if (!targetCRSAuthName.empty()) { if (tryReverseOrder) { sql += "((source_crs_auth_name = ? AND source_crs_code = ?) OR " "(target_crs_auth_name = ? AND target_crs_code = ?)) AND "; params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); } else { sql += "target_crs_auth_name = ? AND target_crs_code = ? AND "; params.emplace_back(targetCRSAuthName); params.emplace_back(targetCRSCode); } } sql += "cov.deprecated = 0"; if (d->hasAuthorityRestriction()) { sql += " AND cov.auth_name = ?"; params.emplace_back(d->authority()); } sql += " ORDER BY pseudo_area_from_swne(south_lat, west_lon, north_lat, " "east_lon) DESC, " "(CASE WHEN accuracy is NULL THEN 1 ELSE 0 END), accuracy"; auto res = d->run(sql, params); std::set> setTransf; if (discardSuperseded) { for (const auto &row : res) { const auto &auth_name = row[4]; const auto &code = row[5]; setTransf.insert( std::pair(auth_name, code)); } } // Do a pass to determine if there are transformations that intersect // intersectingExtent1 & intersectingExtent2 std::vector intersectingTransformations; intersectingTransformations.resize(res.size()); bool hasIntersectingTransformations = false; size_t i = 0; for (const auto &row : res) { size_t thisI = i; ++i; if (discardSuperseded) { const auto &replacement_auth_name = row[11]; const auto &replacement_code = row[12]; if (!replacement_auth_name.empty() && setTransf.find(std::pair( replacement_auth_name, replacement_code)) != setTransf.end()) { // Skip transformations that are superseded by others that got // returned in the result set. continue; } } bool intersecting = true; try { double south_lat = c_locale_stod(row[7]); double west_lon = c_locale_stod(row[8]); double north_lat = c_locale_stod(row[9]); double east_lon = c_locale_stod(row[10]); auto transf_extent = metadata::Extent::createFromBBOX( west_lon, south_lat, east_lon, north_lat); for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { if (extent) { if (!transf_extent->intersects(NN_NO_CHECK(extent))) { intersecting = false; break; } } } } catch (const std::exception &) { } intersectingTransformations[thisI] = intersecting; if (intersecting) hasIntersectingTransformations = true; } // If there are intersecting transformations, then only report those ones // If there are no intersecting transformations, report all of them // This is for the "projinfo -s EPSG:32631 -t EPSG:2171" use case where we // still want to be able to use the Pulkovo datum shift if EPSG:32631 // coordinates are used i = 0; for (const auto &row : res) { size_t thisI = i; ++i; if ((hasIntersectingTransformations || reportOnlyIntersectingTransformations) && !intersectingTransformations[thisI]) { continue; } if (discardSuperseded) { const auto &replacement_auth_name = row[11]; const auto &replacement_code = row[12]; if (!replacement_auth_name.empty() && setTransf.find(std::pair( replacement_auth_name, replacement_code)) != setTransf.end()) { // Skip transformations that are superseded by others that got // returned in the result set. continue; } } const auto &source_crs_auth_name = row[0]; const auto &source_crs_code = row[1]; const auto &target_crs_auth_name = row[2]; const auto &target_crs_code = row[3]; const auto &auth_name = row[4]; const auto &code = row[5]; const auto &table_name = row[6]; auto op = d->createFactory(auth_name)->createCoordinateOperation( code, true, usePROJAlternativeGridNames, table_name); if (tryReverseOrder && (!sourceCRSAuthName.empty() ? (source_crs_auth_name != sourceCRSAuthName || source_crs_code != sourceCRSCode) : (target_crs_auth_name != targetCRSAuthName || target_crs_code != targetCRSCode))) { op = op->inverse(); } if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) { list.emplace_back(op); } } d->context()->d->cache(cacheKey, list); return list; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static bool useIrrelevantPivot(const operation::CoordinateOperationNNPtr &op, const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const std::string &targetCRSAuthName, const std::string &targetCRSCode) { auto concat = dynamic_cast(op.get()); if (!concat) { return false; } auto ops = concat->operations(); for (size_t i = 0; i + 1 < ops.size(); i++) { auto targetCRS = ops[i]->targetCRS(); if (targetCRS) { const auto &ids = targetCRS->identifiers(); if (ids.size() == 1 && ((*ids[0]->codeSpace() == sourceCRSAuthName && ids[0]->code() == sourceCRSCode) || (*ids[0]->codeSpace() == targetCRSAuthName && ids[0]->code() == targetCRSCode))) { return true; } } } return false; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns a list operation::CoordinateOperation between two CRS, * using intermediate codes. * * The list is ordered with preferred operations first. * * Deprecated operations are rejected. * * The method will take care of considering all potential combinations (ie * contrary to createFromCoordinateReferenceSystemCodes(), you do not need to * call it with sourceCRS and targetCRS switched) * * If getAuthority() returns empty, then coordinate operations from all * authorities are considered. * * @param sourceCRSAuthName Authority name of sourceCRSCode * @param sourceCRSCode Source CRS code allocated by authority * sourceCRSAuthName. * @param targetCRSAuthName Authority name of targetCRSCode * @param targetCRSCode Source CRS code allocated by authority * targetCRSAuthName. * @param usePROJAlternativeGridNames Whether PROJ alternative grid names * should be substituted to the official grid names. * @param discardIfMissingGrid Whether coordinate operations that reference * missing grids should be removed from the result set. * @param discardSuperseded Whether cordinate operations that are superseded * (but not deprecated) should be removed from the result set. * @param intermediateCRSAuthCodes List of (auth_name, code) of CRS that can be * used as potential intermediate CRS. If the list is empty, the database will * be used to find common CRS in operations involving both the source and * target CRS. * @param allowedIntermediateObjectType Restrict the type of the intermediate * object considered. * Only ObjectType::CRS and ObjectType::GEOGRAPHIC_CRS supported currently * @param allowedAuthorities One or several authority name allowed for the two * coordinate operations that are going to be searched. When this vector is * no empty, it overrides the authority of this object. This is useful for * example when the coordinate operations to chain belong to two different * allowed authorities. * @param intersectingExtent1 Optional extent that the resulting operations * must intersect. * @param intersectingExtent2 Optional extent that the resulting operations * must intersect. * @return list of coordinate operations * @throw NoSuchAuthorityCodeException * @throw FactoryException */ std::vector AuthorityFactory::createFromCRSCodesWithIntermediates( const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded, const std::vector> &intermediateCRSAuthCodes, ObjectType allowedIntermediateObjectType, const std::vector &allowedAuthorities, const metadata::ExtentPtr &intersectingExtent1, const metadata::ExtentPtr &intersectingExtent2) const { std::vector listTmp; if (sourceCRSAuthName == targetCRSAuthName && sourceCRSCode == targetCRSCode) { return listTmp; } const std::string sqlProlog( discardSuperseded ? "SELECT v1.table_name as table1, " "v1.auth_name AS auth_name1, v1.code AS code1, " "v1.accuracy AS accuracy1, " "v2.table_name as table2, " "v2.auth_name AS auth_name2, v2.code AS code2, " "v2.accuracy as accuracy2, " "a1.south_lat AS south_lat1, " "a1.west_lon AS west_lon1, " "a1.north_lat AS north_lat1, " "a1.east_lon AS east_lon1, " "a2.south_lat AS south_lat2, " "a2.west_lon AS west_lon2, " "a2.north_lat AS north_lat2, " "a2.east_lon AS east_lon2, " "ss1.replacement_auth_name AS replacement_auth_name1, " "ss1.replacement_code AS replacement_code1, " "ss2.replacement_auth_name AS replacement_auth_name2, " "ss2.replacement_code AS replacement_code2 " "FROM coordinate_operation_view v1 " "JOIN coordinate_operation_view v2 " : "SELECT v1.table_name as table1, " "v1.auth_name AS auth_name1, v1.code AS code1, " "v1.accuracy AS accuracy1, " "v2.table_name as table2, " "v2.auth_name AS auth_name2, v2.code AS code2, " "v2.accuracy as accuracy2, " "a1.south_lat AS south_lat1, " "a1.west_lon AS west_lon1, " "a1.north_lat AS north_lat1, " "a1.east_lon AS east_lon1, " "a2.south_lat AS south_lat2, " "a2.west_lon AS west_lon2, " "a2.north_lat AS north_lat2, " "a2.east_lon AS east_lon2 " "FROM coordinate_operation_view v1 " "JOIN coordinate_operation_view v2 "); const std::string joinSupersession( "LEFT JOIN supersession ss1 ON " "ss1.superseded_table_name = v1.table_name AND " "ss1.superseded_auth_name = v1.auth_name AND " "ss1.superseded_code = v1.code AND " "ss1.superseded_table_name = ss1.replacement_table_name " "LEFT JOIN supersession ss2 ON " "ss2.superseded_table_name = v2.table_name AND " "ss2.superseded_auth_name = v2.auth_name AND " "ss2.superseded_code = v2.code AND " "ss2.superseded_table_name = ss2.replacement_table_name "); const std::string joinArea( (discardSuperseded ? joinSupersession : std::string()) + "JOIN area a1 ON v1.area_of_use_auth_name = a1.auth_name " "AND v1.area_of_use_code = a1.code " "JOIN area a2 ON v2.area_of_use_auth_name = a2.auth_name " "AND v2.area_of_use_code = a2.code "); const std::string orderBy( "ORDER BY (CASE WHEN accuracy1 is NULL THEN 1 ELSE 0 END) + " "(CASE WHEN accuracy2 is NULL THEN 1 ELSE 0 END), " "accuracy1 + accuracy2"); // Case (source->intermediate) and (intermediate->target) std::string sql( sqlProlog + "ON v1.target_crs_auth_name = v2.source_crs_auth_name " "AND v1.target_crs_code = v2.source_crs_code " + joinArea + "WHERE v1.source_crs_auth_name = ? AND v1.source_crs_code = ? " "AND v2.target_crs_auth_name = ? AND v2.target_crs_code = ? "); std::string minDate; std::string criterionOnIntermediateCRS; if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { auto sourceCRS = d->createFactory(sourceCRSAuthName) ->createGeodeticCRS(sourceCRSCode); auto targetCRS = d->createFactory(targetCRSAuthName) ->createGeodeticCRS(targetCRSCode); const auto &sourceDatum = sourceCRS->datum(); const auto &targetDatum = targetCRS->datum(); if (sourceDatum && sourceDatum->publicationDate().has_value() && targetDatum && targetDatum->publicationDate().has_value()) { const auto sourceDate(sourceDatum->publicationDate()->toString()); const auto targetDate(targetDatum->publicationDate()->toString()); minDate = std::min(sourceDate, targetDate); // Check that the datum of the intermediateCRS has a publication // date most recent that the one of the source and the target CRS // Except when using the usual WGS84 pivot which happens to have a // NULL publication date. criterionOnIntermediateCRS = "AND EXISTS(SELECT 1 FROM geodetic_crs x " "JOIN geodetic_datum y " "ON " "y.auth_name = x.datum_auth_name AND " "y.code = x.datum_code " "WHERE " "x.auth_name = v1.target_crs_auth_name AND " "x.code = v1.target_crs_code AND " "x.type IN ('geographic 2D', 'geographic 3D') AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } else { criterionOnIntermediateCRS = "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " "x.auth_name = v1.target_crs_auth_name AND " "x.code = v1.target_crs_code AND " "x.type IN ('geographic 2D', 'geographic 3D')) "; } sql += criterionOnIntermediateCRS; } auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode}; std::string additionalWhere( "AND v1.deprecated = 0 AND v2.deprecated = 0 " "AND intersects_bbox(south_lat1, west_lon1, north_lat1, east_lon1, " "south_lat2, west_lon2, north_lat2, east_lon2) = 1 "); if (!allowedAuthorities.empty()) { additionalWhere += "AND v1.auth_name IN ("; for (size_t i = 0; i < allowedAuthorities.size(); i++) { if (i > 0) additionalWhere += ','; additionalWhere += '?'; } additionalWhere += ") AND v2.auth_name IN ("; for (size_t i = 0; i < allowedAuthorities.size(); i++) { if (i > 0) additionalWhere += ','; additionalWhere += '?'; } additionalWhere += ')'; for (const auto &allowedAuthority : allowedAuthorities) { params.emplace_back(allowedAuthority); } for (const auto &allowedAuthority : allowedAuthorities) { params.emplace_back(allowedAuthority); } } if (d->hasAuthorityRestriction()) { additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? "; params.emplace_back(d->authority()); params.emplace_back(d->authority()); } for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { if (extent) { const auto &geogExtent = extent->geographicElements(); if (geogExtent.size() == 1) { auto bbox = dynamic_cast( geogExtent[0].get()); if (bbox) { const double south_lat = bbox->southBoundLatitude(); const double west_lon = bbox->westBoundLongitude(); const double north_lat = bbox->northBoundLatitude(); const double east_lon = bbox->eastBoundLongitude(); if (south_lat != -90.0 || west_lon != -180.0 || north_lat != 90.0 || east_lon != 180.0) { additionalWhere += "AND intersects_bbox(south_lat1, " "west_lon1, north_lat1, east_lon1, ?, ?, ?, ?) AND " "intersects_bbox(south_lat2, west_lon2, " "north_lat2, east_lon2, ?, ?, ?, ?) "; params.emplace_back(south_lat); params.emplace_back(west_lon); params.emplace_back(north_lat); params.emplace_back(east_lon); params.emplace_back(south_lat); params.emplace_back(west_lon); params.emplace_back(north_lat); params.emplace_back(east_lon); } } } } } const auto buildIntermediateWhere = [&intermediateCRSAuthCodes]( const std::string &first_field, const std::string &second_field) { if (intermediateCRSAuthCodes.empty()) { return std::string(); } std::string l_sql(" AND ("); for (size_t i = 0; i < intermediateCRSAuthCodes.size(); ++i) { if (i > 0) { l_sql += " OR"; } l_sql += "(v1." + first_field + "_crs_auth_name = ? AND "; l_sql += "v1." + first_field + "_crs_code = ? AND "; l_sql += "v2." + second_field + "_crs_auth_name = ? AND "; l_sql += "v2." + second_field + "_crs_code = ?) "; } l_sql += ')'; return l_sql; }; std::string intermediateWhere = buildIntermediateWhere("target", "source"); for (const auto &pair : intermediateCRSAuthCodes) { params.emplace_back(pair.first); params.emplace_back(pair.second); params.emplace_back(pair.first); params.emplace_back(pair.second); } auto res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); const auto filterOutSuperseded = [](SQLResultSet &&resultSet) { std::set> setTransf1; std::set> setTransf2; for (const auto &row : resultSet) { // table1 const auto &auth_name1 = row[1]; const auto &code1 = row[2]; // accuracy1 // table2 const auto &auth_name2 = row[5]; const auto &code2 = row[6]; setTransf1.insert( std::pair(auth_name1, code1)); setTransf2.insert( std::pair(auth_name2, code2)); } SQLResultSet filteredResultSet; for (const auto &row : resultSet) { const auto &replacement_auth_name1 = row[16]; const auto &replacement_code1 = row[17]; const auto &replacement_auth_name2 = row[18]; const auto &replacement_code2 = row[19]; if (!replacement_auth_name1.empty() && setTransf1.find(std::pair( replacement_auth_name1, replacement_code1)) != setTransf1.end()) { // Skip transformations that are superseded by others that got // returned in the result set. continue; } if (!replacement_auth_name2.empty() && setTransf2.find(std::pair( replacement_auth_name2, replacement_code2)) != setTransf2.end()) { // Skip transformations that are superseded by others that got // returned in the result set. continue; } filteredResultSet.emplace_back(row); } return filteredResultSet; }; if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; // const auto &accuracy1 = row[3]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata({op1, op2}, false)); } // Case (source->intermediate) and (target->intermediate) sql = sqlProlog + "ON v1.target_crs_auth_name = v2.target_crs_auth_name " "AND v1.target_crs_code = v2.target_crs_code " + joinArea + "WHERE v1.source_crs_auth_name = ? AND v1.source_crs_code = ? " "AND v2.source_crs_auth_name = ? AND v2.source_crs_code = ? "; if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { sql += criterionOnIntermediateCRS; } intermediateWhere = buildIntermediateWhere("target", "target"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; // const auto &accuracy1 = row[3]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata( {op1, op2->inverse()}, false)); } // Case (intermediate->source) and (intermediate->target) sql = sqlProlog + "ON v1.source_crs_auth_name = v2.source_crs_auth_name " "AND v1.source_crs_code = v2.source_crs_code " + joinArea + "WHERE v1.target_crs_auth_name = ? AND v1.target_crs_code = ? " "AND v2.target_crs_auth_name = ? AND v2.target_crs_code = ? "; if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { if (!minDate.empty()) { criterionOnIntermediateCRS = "AND EXISTS(SELECT 1 FROM geodetic_crs x " "JOIN geodetic_datum y " "ON " "y.auth_name = x.datum_auth_name AND " "y.code = x.datum_code " "WHERE " "x.auth_name = v1.source_crs_auth_name AND " "x.code = v1.source_crs_code AND " "x.type IN ('geographic 2D', 'geographic 3D') AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } else { criterionOnIntermediateCRS = "AND EXISTS(SELECT 1 FROM geodetic_crs x WHERE " "x.auth_name = v1.source_crs_auth_name AND " "x.code = v1.source_crs_code AND " "x.type IN ('geographic 2D', 'geographic 3D')) "; } sql += criterionOnIntermediateCRS; } intermediateWhere = buildIntermediateWhere("source", "source"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; // const auto &accuracy1 = row[3]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata( {op1->inverse(), op2}, false)); } // Case (intermediate->source) and (target->intermediate) sql = sqlProlog + "ON v1.source_crs_auth_name = v2.target_crs_auth_name " "AND v1.source_crs_code = v2.target_crs_code " + joinArea + "WHERE v1.target_crs_auth_name = ? AND v1.target_crs_code = ? " "AND v2.source_crs_auth_name = ? AND v2.source_crs_code = ? "; if (allowedIntermediateObjectType == ObjectType::GEOGRAPHIC_CRS) { sql += criterionOnIntermediateCRS; } intermediateWhere = buildIntermediateWhere("source", "target"); res = d->run(sql + additionalWhere + intermediateWhere + orderBy, params); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; // const auto &accuracy1 = row[3]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata( {op1->inverse(), op2->inverse()}, false)); } std::vector list; for (const auto &op : listTmp) { if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) { list.emplace_back(op); } } return list; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::vector AuthorityFactory::createBetweenGeodeticCRSWithDatumBasedIntermediates( const crs::CRSNNPtr &sourceCRS, const std::string &sourceCRSAuthName, const std::string &sourceCRSCode, const crs::CRSNNPtr &targetCRS, const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded, const std::vector &allowedAuthorities, const metadata::ExtentPtr &intersectingExtent1, const metadata::ExtentPtr &intersectingExtent2) const { std::vector listTmp; if (sourceCRSAuthName == targetCRSAuthName && sourceCRSCode == targetCRSCode) { return listTmp; } std::string minDate; const auto &sourceDatum = dynamic_cast(sourceCRS.get())->datum(); const auto &targetDatum = dynamic_cast(targetCRS.get())->datum(); if (sourceDatum && sourceDatum->publicationDate().has_value() && targetDatum && targetDatum->publicationDate().has_value()) { const auto sourceDate(sourceDatum->publicationDate()->toString()); const auto targetDate(targetDatum->publicationDate()->toString()); minDate = std::min(sourceDate, targetDate); } // For some reason, filtering on v1.deprecated and v2.deprecated kills // performance const std::string sqlProlog("SELECT v1.table_name as table1, " "v1.auth_name AS auth_name1, v1.code AS code1, " "v1.deprecated AS deprecated1, " "v2.table_name as table2, " "v2.auth_name AS auth_name2, v2.code AS code2, " "v2.deprecated AS deprecated2 " "FROM coordinate_operation_view v1 " "JOIN coordinate_operation_view v2 " "JOIN geodetic_crs g_source " "JOIN geodetic_crs g_v1s " "JOIN geodetic_crs g_v1t " "JOIN geodetic_crs g_v2s " "JOIN geodetic_crs g_v2t " "JOIN geodetic_crs g_target " "ON g_v1s.auth_name = v1.source_crs_auth_name " "AND g_v1s.code = v1.source_crs_code " "AND g_v1t.auth_name = v1.target_crs_auth_name " "AND g_v1t.code = v1.target_crs_code " "AND g_v2s.auth_name = v2.source_crs_auth_name " "AND g_v2s.code = v2.source_crs_code " "AND g_v2t.auth_name = v2.target_crs_auth_name " "AND g_v2t.code = v2.target_crs_code "); const std::string joinArea( "JOIN area a1 ON v1.area_of_use_auth_name = a1.auth_name " "AND v1.area_of_use_code = a1.code " "JOIN area a2 ON v2.area_of_use_auth_name = a2.auth_name " "AND v2.area_of_use_code = a2.code "); auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode}; std::string additionalWhere(joinArea); additionalWhere += "WHERE g_source.auth_name = ? AND g_source.code = ? " "AND g_target.auth_name = ? AND g_target.code = ? " "AND intersects_bbox(" "a1.south_lat, a1.west_lon, a1.north_lat, a1.east_lon, " "a2.south_lat, a2.west_lon, a2.north_lat, a2.east_lon) = 1 "; #if 0 // While those additonal constraints are correct, they are found to // kill performance. So enforce them as post-processing if (!allowedAuthorities.empty()) { additionalWhere += "AND v1.auth_name IN ("; for (size_t i = 0; i < allowedAuthorities.size(); i++) { if (i > 0) additionalWhere += ','; additionalWhere += '?'; } additionalWhere += ") AND v2.auth_name IN ("; for (size_t i = 0; i < allowedAuthorities.size(); i++) { if (i > 0) additionalWhere += ','; additionalWhere += '?'; } additionalWhere += ") "; for (const auto &allowedAuthority : allowedAuthorities) { params.emplace_back(allowedAuthority); } for (const auto &allowedAuthority : allowedAuthorities) { params.emplace_back(allowedAuthority); } } if (d->hasAuthorityRestriction()) { additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? "; params.emplace_back(d->authority()); params.emplace_back(d->authority()); } #endif for (const auto &extent : {intersectingExtent1, intersectingExtent2}) { if (extent) { const auto &geogExtent = extent->geographicElements(); if (geogExtent.size() == 1) { auto bbox = dynamic_cast( geogExtent[0].get()); if (bbox) { const double south_lat = bbox->southBoundLatitude(); const double west_lon = bbox->westBoundLongitude(); const double north_lat = bbox->northBoundLatitude(); const double east_lon = bbox->eastBoundLongitude(); if (south_lat != -90.0 || west_lon != -180.0 || north_lat != 90.0 || east_lon != 180.0) { additionalWhere += "AND intersects_bbox(a1.south_lat, a1.west_lon, " "a1.north_lat, a1.east_lon, ?, ?, ?, ?) AND " "intersects_bbox(a2.south_lat, a2.west_lon, " "a2.north_lat, a2.east_lon, ?, ?, ?, ?) "; params.emplace_back(south_lat); params.emplace_back(west_lon); params.emplace_back(north_lat); params.emplace_back(east_lon); params.emplace_back(south_lat); params.emplace_back(west_lon); params.emplace_back(north_lat); params.emplace_back(east_lon); } } } } } // Case (source->intermediate) and (intermediate->target) std::string sql(sqlProlog + "AND g_v1t.datum_auth_name = g_v2s.datum_auth_name " "AND g_v1t.datum_code = g_v2s.datum_code " "AND g_v1s.datum_auth_name = g_source.datum_auth_name " "AND g_v1s.datum_code = g_source.datum_code " "AND g_v2t.datum_auth_name = g_target.datum_auth_name " "AND g_v2t.datum_code = g_target.datum_code "); if (!minDate.empty()) { sql += "AND EXISTS(SELECT 1 FROM geodetic_datum y " "WHERE " "y.auth_name = g_v1t.datum_auth_name AND " "y.code = g_v1t.datum_code AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } // fprintf(stderr, "before %s\n", (sql + additionalWhere).c_str()); auto res = d->run(sql + additionalWhere, params); // fprintf(stderr, "after\n"); const auto filterDeprecatedAndNotMatchingAuth = [&](SQLResultSet &&resultSet) { SQLResultSet filteredResultSet; for (const auto &row : resultSet) { const auto &deprecated1 = row[3]; const auto &deprecated2 = row[7]; if (deprecated1 == "1" || deprecated2 == "1") { continue; } const auto &auth_name1 = row[1]; const auto &auth_name2 = row[5]; if (d->hasAuthorityRestriction()) { if (auth_name1 != d->authority() || auth_name2 != d->authority()) { continue; } } if (!allowedAuthorities.empty()) { { bool found = false; for (const auto &auth : allowedAuthorities) { if (auth_name1 == auth) { found = true; break; } } if (!found) { continue; } } { bool found = false; for (const auto &auth : allowedAuthorities) { if (auth_name2 == auth) { found = true; break; } } if (!found) { continue; } } } filteredResultSet.emplace_back(row); } return filteredResultSet; }; const auto filterOutSuperseded = [&](SQLResultSet &&resultSet) { std::set> setTransf; std::string findSupersededSql("SELECT superseded_table_name, " "superseded_auth_name, superseded_code, " "replacement_auth_name, replacement_code " "FROM supersession WHERE "); bool findSupersededFirstWhere = true; ListOfParams findSupersededParams; std::set setAlreadyAsked; const auto keyMapSupersession = []( const std::string &table_name, const std::string &auth_name, const std::string &code) { return table_name + auth_name + code; }; for (const auto &row : resultSet) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto key1 = keyMapSupersession(table1, auth_name1, code1); if (setAlreadyAsked.find(key1) == setAlreadyAsked.end()) { setAlreadyAsked.insert(key1); if (!findSupersededFirstWhere) findSupersededSql += " OR "; findSupersededFirstWhere = false; findSupersededSql += "(superseded_table_name = ? AND replacement_table_name = " "superseded_table_name AND superseded_auth_name = ? AND " "superseded_code = ?)"; findSupersededParams.push_back(table1); findSupersededParams.push_back(auth_name1); findSupersededParams.push_back(code1); } const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; const auto key2 = keyMapSupersession(table2, auth_name2, code2); if (setAlreadyAsked.find(key2) == setAlreadyAsked.end()) { setAlreadyAsked.insert(key2); if (!findSupersededFirstWhere) findSupersededSql += " OR "; findSupersededFirstWhere = false; findSupersededSql += "(superseded_table_name = ? AND replacement_table_name = " "superseded_table_name AND superseded_auth_name = ? AND " "superseded_code = ?)"; findSupersededParams.push_back(table2); findSupersededParams.push_back(auth_name2); findSupersededParams.push_back(code2); } setTransf.insert( std::pair(auth_name1, code1)); setTransf.insert( std::pair(auth_name2, code2)); } std::map>> mapSupersession; if (!findSupersededParams.empty()) { const auto resSuperseded = d->run(findSupersededSql, findSupersededParams); for (const auto &row : resSuperseded) { const auto &superseded_table_name = row[0]; const auto &superseded_auth_name = row[1]; const auto &superseded_code = row[2]; const auto &replacement_auth_name = row[3]; const auto &replacement_code = row[4]; mapSupersession[keyMapSupersession(superseded_table_name, superseded_auth_name, superseded_code)] .push_back(std::pair( replacement_auth_name, replacement_code)); } } SQLResultSet filteredResultSet; for (const auto &row : resultSet) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; auto iter1 = mapSupersession.find( keyMapSupersession(table1, auth_name1, code1)); if (iter1 != mapSupersession.end()) { bool foundReplacement = false; for (const auto &replacement : iter1->second) { const auto &replacement_auth_name = replacement.first; const auto &replacement_code = replacement.second; if (setTransf.find(std::pair( replacement_auth_name, replacement_code)) != setTransf.end()) { // Skip transformations that are superseded by others // that got // returned in the result set. foundReplacement = true; break; } } if (foundReplacement) { continue; } } auto iter2 = mapSupersession.find( keyMapSupersession(table2, auth_name2, code2)); if (iter2 != mapSupersession.end()) { bool foundReplacement = false; for (const auto &replacement : iter2->second) { const auto &replacement_auth_name = replacement.first; const auto &replacement_code = replacement.second; if (setTransf.find(std::pair( replacement_auth_name, replacement_code)) != setTransf.end()) { // Skip transformations that are superseded by others // that got // returned in the result set. foundReplacement = true; break; } } if (foundReplacement) { continue; } } filteredResultSet.emplace_back(row); } return filteredResultSet; }; res = filterDeprecatedAndNotMatchingAuth(std::move(res)); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } auto opFactory = operation::CoordinateOperationFactory::create(); for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } const auto &op1Source = op1->sourceCRS(); const auto &op1Target = op1->targetCRS(); const auto &op2Source = op2->sourceCRS(); const auto &op2Target = op2->targetCRS(); if (op1Source && op1Target && op2Source && op2Target) { std::vector steps; if (!sourceCRS->isEquivalentTo( op1Source.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opFirst = opFactory->createOperation( sourceCRS, NN_NO_CHECK(op1Source)); assert(opFirst); steps.emplace_back(NN_NO_CHECK(opFirst)); } steps.emplace_back(op1); if (!op1Target->isEquivalentTo( op2Source.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opMiddle = opFactory->createOperation( NN_NO_CHECK(op1Target), NN_NO_CHECK(op2Source)); assert(opMiddle); steps.emplace_back(NN_NO_CHECK(opMiddle)); } steps.emplace_back(op2); if (!op2Target->isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opLast = opFactory->createOperation(NN_NO_CHECK(op2Target), targetCRS); assert(opLast); steps.emplace_back(NN_NO_CHECK(opLast)); } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata(steps, false)); } } // Case (source->intermediate) and (target->intermediate) sql = sqlProlog + "AND g_v1t.datum_auth_name = g_v2t.datum_auth_name " "AND g_v1t.datum_code = g_v2t.datum_code " "AND g_v1s.datum_auth_name = g_source.datum_auth_name " "AND g_v1s.datum_code = g_source.datum_code " "AND g_v2s.datum_auth_name = g_target.datum_auth_name " "AND g_v2s.datum_code = g_target.datum_code "; if (!minDate.empty()) { sql += "AND EXISTS(SELECT 1 FROM geodetic_datum y " "WHERE " "y.auth_name = g_v1t.datum_auth_name AND " "y.code = g_v1t.datum_code AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } // fprintf(stderr, "before %s\n", (sql + additionalWhere).c_str()); res = d->run(sql + additionalWhere, params); // fprintf(stderr, "after\n"); res = filterDeprecatedAndNotMatchingAuth(std::move(res)); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } const auto &op1Source = op1->sourceCRS(); const auto &op1Target = op1->targetCRS(); const auto &op2Source = op2->sourceCRS(); const auto &op2Target = op2->targetCRS(); if (op1Source && op1Target && op2Source && op2Target) { std::vector steps; if (!sourceCRS->isEquivalentTo( op1Source.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opFirst = opFactory->createOperation( sourceCRS, NN_NO_CHECK(op1Source)); assert(opFirst); steps.emplace_back(NN_NO_CHECK(opFirst)); } steps.emplace_back(op1); if (!op1Target->isEquivalentTo( op2Target.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opMiddle = opFactory->createOperation( NN_NO_CHECK(op1Target), NN_NO_CHECK(op2Target)); assert(opMiddle); steps.emplace_back(NN_NO_CHECK(opMiddle)); } steps.emplace_back(op2->inverse()); if (!op2Source->isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opLast = opFactory->createOperation(NN_NO_CHECK(op2Source), targetCRS); assert(opLast); steps.emplace_back(NN_NO_CHECK(opLast)); } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata(steps, false)); } } // Case (intermediate->source) and (intermediate->target) sql = sqlProlog + "AND g_v1s.datum_auth_name = g_v2s.datum_auth_name " "AND g_v1s.datum_code = g_v2s.datum_code " "AND g_v1t.datum_auth_name = g_source.datum_auth_name " "AND g_v1t.datum_code = g_source.datum_code " "AND g_v2t.datum_auth_name = g_target.datum_auth_name " "AND g_v2t.datum_code = g_target.datum_code "; if (!minDate.empty()) { sql += "AND EXISTS(SELECT 1 FROM geodetic_datum y " "WHERE " "y.auth_name = g_v1s.datum_auth_name AND " "y.code = g_v1s.datum_code AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } // fprintf(stderr, "before %s\n", (sql + additionalWhere).c_str()); res = d->run(sql + additionalWhere, params); // fprintf(stderr, "after\n"); res = filterDeprecatedAndNotMatchingAuth(std::move(res)); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } const auto &op1Source = op1->sourceCRS(); const auto &op1Target = op1->targetCRS(); const auto &op2Source = op2->sourceCRS(); const auto &op2Target = op2->targetCRS(); if (op1Source && op1Target && op2Source && op2Target) { std::vector steps; if (!sourceCRS->isEquivalentTo( op1Target.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opFirst = opFactory->createOperation( sourceCRS, NN_NO_CHECK(op1Target)); assert(opFirst); steps.emplace_back(NN_NO_CHECK(opFirst)); } steps.emplace_back(op1->inverse()); if (!op1Source->isEquivalentTo( op2Source.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opMiddle = opFactory->createOperation( NN_NO_CHECK(op1Source), NN_NO_CHECK(op2Source)); assert(opMiddle); steps.emplace_back(NN_NO_CHECK(opMiddle)); } steps.emplace_back(op2); if (!op2Target->isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opLast = opFactory->createOperation(NN_NO_CHECK(op2Target), targetCRS); assert(opLast); steps.emplace_back(NN_NO_CHECK(opLast)); } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata(steps, false)); } } // Case (intermediate->source) and (target->intermediate) sql = sqlProlog + "AND g_v1s.datum_auth_name = g_v2t.datum_auth_name " "AND g_v1s.datum_code = g_v2t.datum_code " "AND g_v1t.datum_auth_name = g_source.datum_auth_name " "AND g_v1t.datum_code = g_source.datum_code " "AND g_v2s.datum_auth_name = g_target.datum_auth_name " "AND g_v2s.datum_code = g_target.datum_code "; if (!minDate.empty()) { sql += "AND EXISTS(SELECT 1 FROM geodetic_datum y " "WHERE " "y.auth_name = g_v1s.datum_auth_name AND " "y.code = g_v1s.datum_code AND " "(y.publication_date IS NULL OR " "(y.publication_date >= '" + minDate + "'))) "; } // fprintf(stderr, "before %s\n", (sql + additionalWhere).c_str()); res = d->run(sql + additionalWhere, params); // fprintf(stderr, "after\n"); res = filterDeprecatedAndNotMatchingAuth(std::move(res)); if (discardSuperseded) { res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { const auto &table1 = row[0]; const auto &auth_name1 = row[1]; const auto &code1 = row[2]; const auto &table2 = row[4]; const auto &auth_name2 = row[5]; const auto &code2 = row[6]; auto op1 = d->createFactory(auth_name1) ->createCoordinateOperation( code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) ->createCoordinateOperation( code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } const auto &op1Source = op1->sourceCRS(); const auto &op1Target = op1->targetCRS(); const auto &op2Source = op2->sourceCRS(); const auto &op2Target = op2->targetCRS(); if (op1Source && op1Target && op2Source && op2Target) { std::vector steps; if (!sourceCRS->isEquivalentTo( op1Target.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opFirst = opFactory->createOperation( sourceCRS, NN_NO_CHECK(op1Target)); assert(opFirst); steps.emplace_back(NN_NO_CHECK(opFirst)); } steps.emplace_back(op1->inverse()); if (!op1Source->isEquivalentTo( op2Target.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opMiddle = opFactory->createOperation( NN_NO_CHECK(op1Source), NN_NO_CHECK(op2Target)); assert(opMiddle); steps.emplace_back(NN_NO_CHECK(opMiddle)); } steps.emplace_back(op2->inverse()); if (!op2Source->isEquivalentTo( targetCRS.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opLast = opFactory->createOperation(NN_NO_CHECK(op2Source), targetCRS); assert(opLast); steps.emplace_back(NN_NO_CHECK(opLast)); } listTmp.emplace_back( operation::ConcatenatedOperation::createComputeMetadata(steps, false)); } } std::vector list; for (const auto &op : listTmp) { if (!discardIfMissingGrid || !d->rejectOpDueToMissingGrid(op)) { list.emplace_back(op); } } return list; } //! @endcond // --------------------------------------------------------------------------- /** \brief Returns the authority name associated to this factory. * @return name. */ const std::string &AuthorityFactory::getAuthority() PROJ_PURE_DEFN { return d->authority(); } // --------------------------------------------------------------------------- /** \brief Returns the set of authority codes of the given object type. * * @param type Object type. * @param allowDeprecated whether we should return deprecated objects as well. * @return the set of authority codes for spatial reference objects of the given * type * @throw FactoryException */ std::set AuthorityFactory::getAuthorityCodes(const ObjectType &type, bool allowDeprecated) const { std::string sql; switch (type) { case ObjectType::PRIME_MERIDIAN: sql = "SELECT code FROM prime_meridian WHERE "; break; case ObjectType::ELLIPSOID: sql = "SELECT code FROM ellipsoid WHERE "; break; case ObjectType::DATUM: sql = "SELECT code FROM object_view WHERE table_name IN " "('geodetic_datum', 'vertical_datum') AND "; break; case ObjectType::GEODETIC_REFERENCE_FRAME: sql = "SELECT code FROM geodetic_datum WHERE "; break; case ObjectType::VERTICAL_REFERENCE_FRAME: sql = "SELECT code FROM vertical_datum WHERE "; break; case ObjectType::CRS: sql = "SELECT code FROM crs_view WHERE "; break; case ObjectType::GEODETIC_CRS: sql = "SELECT code FROM geodetic_crs WHERE "; break; case ObjectType::GEOCENTRIC_CRS: sql = "SELECT code FROM geodetic_crs WHERE type " "= " GEOCENTRIC_SINGLE_QUOTED " AND "; break; case ObjectType::GEOGRAPHIC_CRS: sql = "SELECT code FROM geodetic_crs WHERE type IN " "(" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED ") AND "; break; case ObjectType::GEOGRAPHIC_2D_CRS: sql = "SELECT code FROM geodetic_crs WHERE type = " GEOG_2D_SINGLE_QUOTED " AND "; break; case ObjectType::GEOGRAPHIC_3D_CRS: sql = "SELECT code FROM geodetic_crs WHERE type = " GEOG_3D_SINGLE_QUOTED " AND "; break; case ObjectType::VERTICAL_CRS: sql = "SELECT code FROM vertical_crs WHERE "; break; case ObjectType::PROJECTED_CRS: sql = "SELECT code FROM projected_crs WHERE "; break; case ObjectType::COMPOUND_CRS: sql = "SELECT code FROM compound_crs WHERE "; break; case ObjectType::COORDINATE_OPERATION: sql = "SELECT code FROM coordinate_operation_with_conversion_view WHERE "; break; case ObjectType::CONVERSION: sql = "SELECT code FROM conversion WHERE "; break; case ObjectType::TRANSFORMATION: sql = "SELECT code FROM coordinate_operation_view WHERE table_name != " "'concatenated_operation' AND "; break; case ObjectType::CONCATENATED_OPERATION: sql = "SELECT code FROM concatenated_operation WHERE "; break; } sql += "auth_name = ?"; if (!allowDeprecated) { sql += " AND deprecated = 0"; } auto res = d->run(sql, {d->authority()}); std::set set; for (const auto &row : res) { set.insert(row[0]); } return set; } // --------------------------------------------------------------------------- /** \brief Gets a description of the object corresponding to a code. * * \note In case of several objects of different types with the same code, * one of them will be arbitrarily selected. But if a CRS object is found, it * will be selected. * * @param code Object code allocated by authority. (e.g. "4326") * @return description. * @throw NoSuchAuthorityCodeException * @throw FactoryException */ std::string AuthorityFactory::getDescriptionText(const std::string &code) const { auto sql = "SELECT name, table_name FROM object_view WHERE auth_name = ? " "AND code = ? ORDER BY table_name"; auto sqlRes = d->runWithCodeParam(sql, code); if (sqlRes.empty()) { throw NoSuchAuthorityCodeException("object not found", d->authority(), code); } std::string text; for (const auto &row : sqlRes) { const auto &tableName = row[1]; if (tableName == "geodetic_crs" || tableName == "projected_crs" || tableName == "vertical_crs" || tableName == "compound_crs") { return row[0]; } else if (text.empty()) { text = row[0]; } } return text; } // --------------------------------------------------------------------------- /** \brief Return a list of information on CRS objects * * This is functionnaly equivalent of listing the codes from an authority, * instantiating * a CRS object for each of them and getting the information from this CRS * object, but this implementation has much less overhead. * * @throw FactoryException */ std::list AuthorityFactory::getCRSInfoList() const { std::string sql = "SELECT c.auth_name, c.code, c.name, c.type, " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.name, NULL FROM geodetic_crs c " "JOIN area a ON " "c.area_of_use_auth_name = a.auth_name AND " "c.area_of_use_code = a.code"; ListOfParams params; if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); } sql += " UNION ALL "; sql += "SELECT c.auth_name, c.code, c.name, 'projected', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.name, cm.name AS conversion_method_name FROM projected_crs c " "JOIN area a ON " "c.area_of_use_auth_name = a.auth_name AND " "c.area_of_use_code = a.code " "LEFT JOIN conversion_table conv ON " "c.conversion_auth_name = conv.auth_name AND " "c.conversion_code = conv.code " "LEFT JOIN conversion_method cm ON " "conv.method_auth_name = cm.auth_name AND " "conv.method_code = cm.code"; if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); } sql += " UNION ALL "; sql += "SELECT c.auth_name, c.code, c.name, 'vertical', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.name, NULL FROM vertical_crs c " "JOIN area a ON " "c.area_of_use_auth_name = a.auth_name AND " "c.area_of_use_code = a.code"; if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); } sql += " UNION ALL "; sql += "SELECT c.auth_name, c.code, c.name, 'compound', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.name, NULL FROM compound_crs c " "JOIN area a ON " "c.area_of_use_auth_name = a.auth_name AND " "c.area_of_use_code = a.code"; if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); } auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { AuthorityFactory::CRSInfo info; info.authName = row[0]; info.code = row[1]; info.name = row[2]; const auto &type = row[3]; if (type == GEOG_2D) { info.type = AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS; } else if (type == GEOG_3D) { info.type = AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS; } else if (type == GEOCENTRIC) { info.type = AuthorityFactory::ObjectType::GEOCENTRIC_CRS; } else if (type == PROJECTED) { info.type = AuthorityFactory::ObjectType::PROJECTED_CRS; } else if (type == VERTICAL) { info.type = AuthorityFactory::ObjectType::VERTICAL_CRS; } else if (type == COMPOUND) { info.type = AuthorityFactory::ObjectType::COMPOUND_CRS; } info.deprecated = row[4] == "1"; if (row[5].empty()) { info.bbox_valid = false; } else { info.bbox_valid = true; info.west_lon_degree = c_locale_stod(row[5]); info.south_lat_degree = c_locale_stod(row[6]); info.east_lon_degree = c_locale_stod(row[7]); info.north_lat_degree = c_locale_stod(row[8]); } info.areaName = row[9]; info.projectionMethodName = row[10]; res.emplace_back(info); } return res; } // --------------------------------------------------------------------------- /** \brief Gets the official name from a possibly alias name. * * @param aliasedName Alias name. * @param tableName Table name/category. Can help in case of ambiguities. * Or empty otherwise. * @param source Source of the alias. Can help in case of ambiguities. * Or empty otherwise. * @param tryEquivalentNameSpelling whether the comparison of aliasedName with * the alt_name column of the alis_name table should be done with using * metadata::Identifier::isEquivalentName() rather than strict string * comparison; * @param outTableName Table name in which the official name has been found. * @param outAuthName Authority name of the official name that has been found. * @param outCode Code of the official name that has been found. * @return official name (or empty if not found). * @throw FactoryException */ std::string AuthorityFactory::getOfficialNameFromAlias( const std::string &aliasedName, const std::string &tableName, const std::string &source, bool tryEquivalentNameSpelling, std::string &outTableName, std::string &outAuthName, std::string &outCode) const { if (tryEquivalentNameSpelling) { std::string sql( "SELECT table_name, auth_name, code, alt_name FROM alias_name"); ListOfParams params; if (!tableName.empty()) { sql += " WHERE table_name = ?"; params.push_back(tableName); } if (!source.empty()) { if (!tableName.empty()) { sql += " AND "; } else { sql += " WHERE "; } sql += "source = ?"; params.push_back(source); } auto res = d->run(sql, params); if (res.empty()) { return std::string(); } for (const auto &row : res) { const auto &alt_name = row[3]; if (metadata::Identifier::isEquivalentName(alt_name.c_str(), aliasedName.c_str())) { outTableName = row[0]; outAuthName = row[1]; outCode = row[2]; sql = "SELECT name FROM \""; sql += replaceAll(outTableName, "\"", "\"\""); sql += "\" WHERE auth_name = ? AND code = ?"; res = d->run(sql, {outAuthName, outCode}); if (res.empty()) { // shouldn't happen normally return std::string(); } return res.front()[0]; } } return std::string(); } else { std::string sql( "SELECT table_name, auth_name, code FROM alias_name WHERE " "alt_name = ?"); ListOfParams params{aliasedName}; if (!tableName.empty()) { sql += " AND table_name = ?"; params.push_back(tableName); } if (!source.empty()) { sql += " AND source = ?"; params.push_back(source); } auto res = d->run(sql, params); if (res.empty()) { return std::string(); } const auto &row = res.front(); outTableName = row[0]; outAuthName = row[1]; outCode = row[2]; sql = "SELECT name FROM \""; sql += replaceAll(outTableName, "\"", "\"\""); sql += "\" WHERE auth_name = ? AND code = ?"; res = d->run(sql, {outAuthName, outCode}); if (res.empty()) { // shouldn't happen normally return std::string(); } return res.front()[0]; } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static void addToListString(std::string &out, const char *in) { if (!out.empty()) { out += ','; } out += in; } static void addToListStringWithOR(std::string &out, const char *in) { if (!out.empty()) { out += " OR "; } out += in; } //! @endcond // --------------------------------------------------------------------------- /** \brief Return a list of objects by their name * * @param searchedName Searched name. Must be at least 2 character long. * @param allowedObjectTypes List of object types into which to search. If * empty, all object types will be searched. * @param approximateMatch Whether approximate name identification is allowed. * @param limitResultCount Maximum number of results to return. * Or 0 for unlimited. * @return list of matched objects. * @throw FactoryException */ std::list AuthorityFactory::createObjectsFromName( const std::string &searchedName, const std::vector &allowedObjectTypes, bool approximateMatch, size_t limitResultCount) const { std::string searchedNameWithoutDeprecated(searchedName); bool deprecated = false; if (ends_with(searchedNameWithoutDeprecated, " (deprecated)")) { deprecated = true; searchedNameWithoutDeprecated.resize( searchedNameWithoutDeprecated.size() - strlen(" (deprecated)")); } const std::string canonicalizedSearchedName( metadata::Identifier::canonicalizeName(searchedNameWithoutDeprecated)); if (canonicalizedSearchedName.size() <= 1) { return {}; } std::string sql( "SELECT table_name, auth_name, code, name, deprecated FROM object_view " "WHERE "); if (deprecated) { sql += "deprecated = 1 AND "; } ListOfParams params; if (!approximateMatch) { sql += "name LIKE ? AND "; params.push_back(searchedNameWithoutDeprecated); } if (d->hasAuthorityRestriction()) { sql += "auth_name = ? AND "; params.emplace_back(d->authority()); } if (allowedObjectTypes.empty()) { sql += "table_name IN (" "'prime_meridian','ellipsoid','geodetic_datum'," "'vertical_datum','geodetic_crs','projected_crs'," "'vertical_crs','compound_crs','conversion'," "'helmert_transformation','grid_transformation'," "'other_transformation','concatenated_operation'" ")"; } else { std::string tableNameList; std::string otherConditions; for (const auto type : allowedObjectTypes) { switch (type) { case ObjectType::PRIME_MERIDIAN: addToListString(tableNameList, "'prime_meridian'"); break; case ObjectType::ELLIPSOID: addToListString(tableNameList, "'ellipsoid'"); break; case ObjectType::DATUM: addToListString(tableNameList, "'geodetic_datum','vertical_datum'"); break; case ObjectType::GEODETIC_REFERENCE_FRAME: addToListString(tableNameList, "'geodetic_datum'"); break; case ObjectType::VERTICAL_REFERENCE_FRAME: addToListString(tableNameList, "'vertical_datum'"); break; case ObjectType::CRS: addToListString(tableNameList, "'geodetic_crs','projected_crs'," "'vertical_crs','compound_crs'"); break; case ObjectType::GEODETIC_CRS: addToListString(tableNameList, "'geodetic_crs'"); break; case ObjectType::GEOCENTRIC_CRS: addToListStringWithOR(otherConditions, "(table_name = " GEOCENTRIC_SINGLE_QUOTED " AND " "type = " GEOCENTRIC_SINGLE_QUOTED ")"); break; case ObjectType::GEOGRAPHIC_CRS: addToListStringWithOR(otherConditions, "(table_name = 'geodetic_crs' AND " "type IN (" GEOG_2D_SINGLE_QUOTED "," GEOG_3D_SINGLE_QUOTED "))"); break; case ObjectType::GEOGRAPHIC_2D_CRS: addToListStringWithOR(otherConditions, "(table_name = 'geodetic_crs' AND " "type = " GEOG_2D_SINGLE_QUOTED ")"); break; case ObjectType::GEOGRAPHIC_3D_CRS: addToListStringWithOR(otherConditions, "(table_name = 'geodetic_crs' AND " "type = " GEOG_3D_SINGLE_QUOTED ")"); break; case ObjectType::PROJECTED_CRS: addToListString(tableNameList, "'projected_crs'"); break; case ObjectType::VERTICAL_CRS: addToListString(tableNameList, "'vertical_crs'"); break; case ObjectType::COMPOUND_CRS: addToListString(tableNameList, "'compound_crs'"); break; case ObjectType::COORDINATE_OPERATION: addToListString(tableNameList, "'conversion','helmert_transformation'," "'grid_transformation','other_transformation'," "'concatenated_operation'"); break; case ObjectType::CONVERSION: addToListString(tableNameList, "'conversion'"); break; case ObjectType::TRANSFORMATION: addToListString(tableNameList, "'helmert_transformation'," "'grid_transformation','other_transformation'"); break; case ObjectType::CONCATENATED_OPERATION: addToListString(tableNameList, "'concatenated_operation'"); break; } } if (!tableNameList.empty()) { sql += "((table_name IN ("; sql += tableNameList; sql += "))"; if (!otherConditions.empty()) { sql += " OR "; sql += otherConditions; } sql += ')'; } else if (!otherConditions.empty()) { sql += "("; sql += otherConditions; sql += ')'; } } sql += " ORDER BY deprecated, length(name), name"; if (limitResultCount > 0 && limitResultCount < static_cast(std::numeric_limits::max()) && !approximateMatch) { sql += " LIMIT "; sql += toString(static_cast(limitResultCount)); } std::list res; // Querying geodetic datum is a super hot path when importing from WKT1 // so cache results. if (allowedObjectTypes.size() == 1 && allowedObjectTypes[0] == ObjectType::GEODETIC_REFERENCE_FRAME && approximateMatch && d->authority().empty()) { auto &mapCanonicalizeGRFName = d->context()->getPrivate()->getMapCanonicalizeGRFName(); if (mapCanonicalizeGRFName.empty()) { auto sqlRes = d->run(sql, params); for (const auto &row : sqlRes) { const auto &name = row[3]; const auto &deprecatedStr = row[4]; const auto canonicalizedName( metadata::Identifier::canonicalizeName(name)); auto &v = mapCanonicalizeGRFName[canonicalizedName]; if (deprecatedStr == "0" || v.empty() || v.front()[4] == "1") { v.push_back(row); } } } auto iter = mapCanonicalizeGRFName.find(canonicalizedSearchedName); if (iter != mapCanonicalizeGRFName.end()) { const auto &listOfRow = iter->second; for (const auto &row : listOfRow) { const auto &auth_name = row[1]; const auto &code = row[2]; auto factory = d->createFactory(auth_name); res.emplace_back(factory->createGeodeticDatum(code)); if (limitResultCount > 0 && res.size() == limitResultCount) { break; } } } else { for (const auto &pair : mapCanonicalizeGRFName) { const auto &listOfRow = pair.second; for (const auto &row : listOfRow) { const auto &name = row[3]; bool match = ci_find(name, searchedNameWithoutDeprecated) != std::string::npos; if (!match) { const auto &canonicalizedName(pair.first); match = ci_find(canonicalizedName, canonicalizedSearchedName) != std::string::npos; } if (!match) { continue; } const auto &auth_name = row[1]; const auto &code = row[2]; auto factory = d->createFactory(auth_name); res.emplace_back(factory->createGeodeticDatum(code)); if (limitResultCount > 0 && res.size() == limitResultCount) { break; } } if (limitResultCount > 0 && res.size() == limitResultCount) { break; } } } } else { auto sqlRes = d->run(sql, params); bool isFirst = true; bool firstIsDeprecated = false; for (const auto &row : sqlRes) { const auto &name = row[3]; if (approximateMatch) { bool match = ci_find(name, searchedNameWithoutDeprecated) != std::string::npos; if (!match) { const auto canonicalizedName( metadata::Identifier::canonicalizeName(name)); match = ci_find(canonicalizedName, canonicalizedSearchedName) != std::string::npos; } if (!match) { continue; } } const auto &table_name = row[0]; const auto &auth_name = row[1]; const auto &code = row[2]; const auto &deprecatedStr = row[4]; if (isFirst) { firstIsDeprecated = deprecatedStr == "1"; isFirst = false; } if (deprecatedStr == "1" && !res.empty() && !firstIsDeprecated) { break; } auto factory = d->createFactory(auth_name); if (table_name == "prime_meridian") { res.emplace_back(factory->createPrimeMeridian(code)); } else if (table_name == "ellipsoid") { res.emplace_back(factory->createEllipsoid(code)); } else if (table_name == "geodetic_datum") { res.emplace_back(factory->createGeodeticDatum(code)); } else if (table_name == "vertical_datum") { res.emplace_back(factory->createVerticalDatum(code)); } else if (table_name == "geodetic_crs") { res.emplace_back(factory->createGeodeticCRS(code)); } else if (table_name == "projected_crs") { res.emplace_back(factory->createProjectedCRS(code)); } else if (table_name == "vertical_crs") { res.emplace_back(factory->createVerticalCRS(code)); } else if (table_name == "compound_crs") { res.emplace_back(factory->createCompoundCRS(code)); } else if (table_name == "conversion") { res.emplace_back(factory->createConversion(code)); } else if (table_name == "grid_transformation" || table_name == "helmert_transformation" || table_name == "other_transformation" || table_name == "concatenated_operation") { res.emplace_back( factory->createCoordinateOperation(code, true)); } else { assert(false); } if (limitResultCount > 0 && res.size() == limitResultCount) { break; } } } auto sortLambda = [](const common::IdentifiedObjectNNPtr &a, const common::IdentifiedObjectNNPtr &b) { const auto &aName = a->nameStr(); const auto &bName = b->nameStr(); if (aName.size() < bName.size()) { return true; } if (aName.size() > bName.size()) { return false; } const auto &aIds = a->identifiers(); const auto &bIds = b->identifiers(); if (aIds.size() < bIds.size()) { return true; } if (aIds.size() > bIds.size()) { return false; } for (size_t idx = 0; idx < aIds.size(); idx++) { const auto &aCodeSpace = *aIds[idx]->codeSpace(); const auto &bCodeSpace = *bIds[idx]->codeSpace(); const auto codeSpaceComparison = aCodeSpace.compare(bCodeSpace); if (codeSpaceComparison < 0) { return true; } if (codeSpaceComparison > 0) { return false; } const auto &aCode = aIds[idx]->code(); const auto &bCode = bIds[idx]->code(); const auto codeComparison = aCode.compare(bCode); if (codeComparison < 0) { return true; } if (codeComparison > 0) { return false; } } return strcmp(typeid(a.get()).name(), typeid(b.get()).name()) < 0; }; res.sort(sortLambda); return res; } // --------------------------------------------------------------------------- /** \brief Return a list of area of use from their name * * @param name Searched name. * @param approximateMatch Whether approximate name identification is allowed. * @return list of (auth_name, code) of matched objects. * @throw FactoryException */ std::list> AuthorityFactory::listAreaOfUseFromName(const std::string &name, bool approximateMatch) const { std::string sql( "SELECT auth_name, code FROM area WHERE deprecated = 0 AND "); ListOfParams params; if (d->hasAuthorityRestriction()) { sql += " auth_name = ? AND "; params.emplace_back(d->authority()); } sql += "name LIKE ?"; if (!approximateMatch) { params.push_back(name); } else { params.push_back('%' + name + '%'); } auto sqlRes = d->run(sql, params); std::list> res; for (const auto &row : sqlRes) { res.emplace_back(row[0], row[1]); } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list AuthorityFactory::createEllipsoidFromExisting( const datum::EllipsoidNNPtr &ellipsoid) const { std::string sql( "SELECT auth_name, code FROM ellipsoid WHERE " "abs(semi_major_axis - ?) < 1e-10 * abs(semi_major_axis) AND " "((semi_minor_axis IS NOT NULL AND " "abs(semi_minor_axis - ?) < 1e-10 * abs(semi_minor_axis)) OR " "((inv_flattening IS NOT NULL AND " "abs(inv_flattening - ?) < 1e-10 * abs(inv_flattening))))"); ListOfParams params{ellipsoid->semiMajorAxis().getSIValue(), ellipsoid->computeSemiMinorAxis().getSIValue(), ellipsoid->computedInverseFlattening()}; auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createEllipsoid(code)); } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list AuthorityFactory::createGeodeticCRSFromDatum( const std::string &datum_auth_name, const std::string &datum_code, const std::string &geodetic_crs_type) const { std::string sql( "SELECT auth_name, code FROM geodetic_crs WHERE " "datum_auth_name = ? AND datum_code = ? AND deprecated = 0"); ListOfParams params{datum_auth_name, datum_code}; if (d->hasAuthorityRestriction()) { sql += " AND auth_name = ?"; params.emplace_back(d->authority()); } if (!geodetic_crs_type.empty()) { sql += " AND type = ?"; params.emplace_back(geodetic_crs_type); } sql += " ORDER BY auth_name, code"; auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createGeodeticCRS(code)); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list AuthorityFactory::createVerticalCRSFromDatum( const std::string &datum_auth_name, const std::string &datum_code) const { std::string sql( "SELECT auth_name, code FROM vertical_crs WHERE " "datum_auth_name = ? AND datum_code = ? AND deprecated = 0"); ListOfParams params{datum_auth_name, datum_code}; if (d->hasAuthorityRestriction()) { sql += " AND auth_name = ?"; params.emplace_back(d->authority()); } auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createVerticalCRS(code)); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list AuthorityFactory::createGeodeticCRSFromEllipsoid( const std::string &ellipsoid_auth_name, const std::string &ellipsoid_code, const std::string &geodetic_crs_type) const { std::string sql( "SELECT geodetic_crs.auth_name, geodetic_crs.code FROM geodetic_crs " "JOIN geodetic_datum ON " "geodetic_crs.datum_auth_name = geodetic_datum.auth_name AND " "geodetic_crs.datum_code = geodetic_datum.code WHERE " "geodetic_datum.ellipsoid_auth_name = ? AND " "geodetic_datum.ellipsoid_code = ? AND " "geodetic_datum.deprecated = 0 AND " "geodetic_crs.deprecated = 0"); ListOfParams params{ellipsoid_auth_name, ellipsoid_code}; if (d->hasAuthorityRestriction()) { sql += " AND geodetic_crs.auth_name = ?"; params.emplace_back(d->authority()); } if (!geodetic_crs_type.empty()) { sql += " AND geodetic_crs.type = ?"; params.emplace_back(geodetic_crs_type); } auto sqlRes = d->run(sql, params); std::list res; for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createGeodeticCRS(code)); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress static std::string buildSqlLookForAuthNameCode( const std::list> &list, ListOfParams ¶ms, const char *prefixField) { std::string sql("("); std::set authorities; for (const auto &crs : list) { auto boundCRS = dynamic_cast(crs.first.get()); const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers() : crs.first->identifiers(); if (!ids.empty()) { authorities.insert(*(ids[0]->codeSpace())); } } bool firstAuth = true; for (const auto &auth_name : authorities) { if (!firstAuth) { sql += " OR "; } firstAuth = false; sql += "( "; sql += prefixField; sql += "auth_name = ? AND "; sql += prefixField; sql += "code IN ("; params.emplace_back(auth_name); bool firstGeodCRSForAuth = true; for (const auto &crs : list) { auto boundCRS = dynamic_cast(crs.first.get()); const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers() : crs.first->identifiers(); if (!ids.empty() && *(ids[0]->codeSpace()) == auth_name) { if (!firstGeodCRSForAuth) { sql += ','; } firstGeodCRSForAuth = false; sql += '?'; params.emplace_back(ids[0]->code()); } } sql += "))"; } sql += ')'; return sql; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::list AuthorityFactory::createProjectedCRSFromExisting( const crs::ProjectedCRSNNPtr &crs) const { std::list res; const auto &conv = crs->derivingConversionRef(); const auto &method = conv->method(); const auto methodEPSGCode = method->getEPSGCode(); if (methodEPSGCode == 0) { return res; } auto lockedThisFactory(d->getSharedFromThis()); assert(lockedThisFactory); const auto &baseCRS(crs->baseCRS()); auto candidatesGeodCRS = baseCRS->crs::CRS::identify(lockedThisFactory); auto geogCRS = dynamic_cast(baseCRS.get()); if (geogCRS) { const auto axisOrder = geogCRS->coordinateSystem()->axisOrder(); if (axisOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH || axisOrder == cs::EllipsoidalCS::AxisOrder::LAT_NORTH_LONG_EAST) { const auto &unit = geogCRS->coordinateSystem()->axisList()[0]->unit(); auto otherOrderGeogCRS = crs::GeographicCRS::create( util::PropertyMap().set(common::IdentifiedObject::NAME_KEY, geogCRS->nameStr()), geogCRS->datum(), geogCRS->datumEnsemble(), axisOrder == cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH ? cs::EllipsoidalCS::createLatitudeLongitude(unit) : cs::EllipsoidalCS::createLongitudeLatitude(unit)); auto otherCandidatesGeodCRS = otherOrderGeogCRS->crs::CRS::identify(lockedThisFactory); candidatesGeodCRS.insert(candidatesGeodCRS.end(), otherCandidatesGeodCRS.begin(), otherCandidatesGeodCRS.end()); } } std::string sql( "SELECT projected_crs.auth_name, projected_crs.code FROM projected_crs " "JOIN conversion_table conv ON " "projected_crs.conversion_auth_name = conv.auth_name AND " "projected_crs.conversion_code = conv.code WHERE " "projected_crs.deprecated = 0 AND "); ListOfParams params; if (!candidatesGeodCRS.empty()) { sql += buildSqlLookForAuthNameCode(candidatesGeodCRS, params, "projected_crs.geodetic_crs_"); sql += " AND "; } sql += "conv.method_auth_name = 'EPSG' AND " "conv.method_code = ?"; params.emplace_back(toString(methodEPSGCode)); if (d->hasAuthorityRestriction()) { sql += " AND projected_crs.auth_name = ?"; params.emplace_back(d->authority()); } int iParam = 1; for (const auto &genOpParamvalue : conv->parameterValues()) { auto opParamvalue = dynamic_cast( genOpParamvalue.get()); if (!opParamvalue) { break; } const auto paramEPSGCode = opParamvalue->parameter()->getEPSGCode(); const auto ¶meterValue = opParamvalue->parameterValue(); if (!(paramEPSGCode > 0 && parameterValue->type() == operation::ParameterValue::Type::MEASURE)) { break; } const auto &measure = parameterValue->value(); const auto &unit = measure.unit(); if (unit == common::UnitOfMeasure::DEGREE && geogCRS->coordinateSystem()->axisList()[0]->unit() == unit) { const auto iParamAsStr(toString(iParam)); sql += " AND conv.param"; sql += iParamAsStr; sql += "_code = ? AND conv.param"; sql += iParamAsStr; sql += "_auth_name = 'EPSG' AND conv.param"; sql += iParamAsStr; sql += "_value BETWEEN ? AND ?"; // As angles might be expressed with the odd unit EPSG:9110 // "sexagesimal DMS", we have to provide a broad range params.emplace_back(toString(paramEPSGCode)); params.emplace_back(measure.value() - 1); params.emplace_back(measure.value() + 1); } iParam++; } auto sqlRes = d->run(sql, params); params.clear(); sql = "SELECT auth_name, code FROM projected_crs WHERE " "deprecated = 0 AND conversion_auth_name IS NULL AND "; if (!candidatesGeodCRS.empty()) { sql += buildSqlLookForAuthNameCode(candidatesGeodCRS, params, "geodetic_crs_"); sql += " AND "; } const auto escapeLikeStr = [](const std::string &str) { return replaceAll(replaceAll(replaceAll(str, "\\", "\\\\"), "_", "\\_"), "%", "\\%"); }; const auto ellpsSemiMajorStr = toString(baseCRS->ellipsoid()->semiMajorAxis().getSIValue(), 10); sql += "(text_definition LIKE ? ESCAPE '\\'"; // WKT2 definition { std::string patternVal("%"); patternVal += ','; patternVal += ellpsSemiMajorStr; patternVal += '%'; patternVal += escapeLikeStr(method->nameStr()); patternVal += '%'; params.emplace_back(patternVal); } const auto *mapping = getMapping(method.get()); if (mapping && mapping->proj_name_main) { sql += " OR (text_definition LIKE ? AND (text_definition LIKE ?"; std::string patternVal("%"); patternVal += "proj="; patternVal += mapping->proj_name_main; patternVal += '%'; params.emplace_back(patternVal); // could be a= or R= patternVal = "%="; patternVal += ellpsSemiMajorStr; patternVal += '%'; params.emplace_back(patternVal); std::string projEllpsName; std::string ellpsName; if (baseCRS->ellipsoid()->lookForProjWellKnownEllps(projEllpsName, ellpsName)) { sql += " OR text_definition LIKE ?"; // Could be ellps= or datum= patternVal = "%="; patternVal += projEllpsName; patternVal += '%'; params.emplace_back(patternVal); } sql += "))"; } // WKT1_GDAL definition const char *wkt1GDALMethodName = conv->getWKT1GDALMethodName(); if (wkt1GDALMethodName) { sql += " OR text_definition LIKE ? ESCAPE '\\'"; std::string patternVal("%"); patternVal += ','; patternVal += ellpsSemiMajorStr; patternVal += '%'; patternVal += escapeLikeStr(wkt1GDALMethodName); patternVal += '%'; params.emplace_back(patternVal); } // WKT1_ESRI definition const char *esriMethodName = conv->getESRIMethodName(); if (esriMethodName) { sql += " OR text_definition LIKE ? ESCAPE '\\'"; std::string patternVal("%"); patternVal += ','; patternVal += ellpsSemiMajorStr; patternVal += '%'; patternVal += escapeLikeStr(esriMethodName); patternVal += '%'; auto fe = &conv->parameterValueMeasure(EPSG_CODE_PARAMETER_FALSE_EASTING); if (*fe == Measure()) { fe = &conv->parameterValueMeasure( EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN); } if (!(*fe == Measure())) { patternVal += "PARAMETER[\"False\\_Easting\","; patternVal += toString(fe->convertToUnit( crs->coordinateSystem()->axisList()[0]->unit()), 10); patternVal += '%'; } auto lat = &conv->parameterValueMeasure( EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN); if (*lat == Measure()) { lat = &conv->parameterValueMeasure( EPSG_NAME_PARAMETER_LATITUDE_FALSE_ORIGIN); } if (!(*lat == Measure())) { patternVal += "PARAMETER[\"Latitude\\_Of\\_Origin\","; const auto &angularUnit = dynamic_cast(crs->baseCRS().get()) ? crs->baseCRS()->coordinateSystem()->axisList()[0]->unit() : UnitOfMeasure::DEGREE; patternVal += toString(lat->convertToUnit(angularUnit), 10); patternVal += '%'; } params.emplace_back(patternVal); } sql += ")"; if (d->hasAuthorityRestriction()) { sql += " AND auth_name = ?"; params.emplace_back(d->authority()); } auto sqlRes2 = d->run(sql, params); if (sqlRes.size() <= 200) { for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back( d->createFactory(auth_name)->createProjectedCRS(code)); } } if (sqlRes2.size() <= 200) { for (const auto &row : sqlRes2) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back( d->createFactory(auth_name)->createProjectedCRS(code)); } } return res; } // --------------------------------------------------------------------------- std::list AuthorityFactory::createCompoundCRSFromExisting( const crs::CompoundCRSNNPtr &crs) const { std::list res; auto lockedThisFactory(d->getSharedFromThis()); assert(lockedThisFactory); const auto &components = crs->componentReferenceSystems(); if (components.size() != 2) { return res; } auto candidatesHorizCRS = components[0]->identify(lockedThisFactory); auto candidatesVertCRS = components[1]->identify(lockedThisFactory); if (candidatesHorizCRS.empty() && candidatesVertCRS.empty()) { return res; } std::string sql("SELECT auth_name, code FROM compound_crs WHERE " "deprecated = 0 AND "); ListOfParams params; bool addAnd = false; if (!candidatesHorizCRS.empty()) { sql += buildSqlLookForAuthNameCode(candidatesHorizCRS, params, "horiz_crs_"); addAnd = true; } if (!candidatesVertCRS.empty()) { if (addAnd) { sql += " AND "; } sql += buildSqlLookForAuthNameCode(candidatesVertCRS, params, "vertical_crs_"); addAnd = true; } if (d->hasAuthorityRestriction()) { if (addAnd) { sql += " AND "; } sql += "auth_name = ?"; params.emplace_back(d->authority()); } auto sqlRes = d->run(sql, params); for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createCompoundCRS(code)); } return res; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::vector AuthorityFactory::getTransformationsForGeoid( const std::string &geoidName, bool usePROJAlternativeGridNames) const { std::vector res; const std::string sql("SELECT operation_auth_name, operation_code FROM " "geoid_model WHERE name = ?"); auto sqlRes = d->run(sql, {geoidName}); for (const auto &row : sqlRes) { const auto &auth_name = row[0]; const auto &code = row[1]; res.emplace_back(d->createFactory(auth_name)->createCoordinateOperation( code, usePROJAlternativeGridNames)); } return res; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress FactoryException::FactoryException(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- FactoryException::FactoryException(const std::string &message) : Exception(message) {} // --------------------------------------------------------------------------- FactoryException::~FactoryException() = default; // --------------------------------------------------------------------------- FactoryException::FactoryException(const FactoryException &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct NoSuchAuthorityCodeException::Private { std::string authority_; std::string code_; Private(const std::string &authority, const std::string &code) : authority_(authority), code_(code) {} }; // --------------------------------------------------------------------------- NoSuchAuthorityCodeException::NoSuchAuthorityCodeException( const std::string &message, const std::string &authority, const std::string &code) : FactoryException(message), d(internal::make_unique(authority, code)) {} // --------------------------------------------------------------------------- NoSuchAuthorityCodeException::~NoSuchAuthorityCodeException() = default; // --------------------------------------------------------------------------- NoSuchAuthorityCodeException::NoSuchAuthorityCodeException( const NoSuchAuthorityCodeException &other) : FactoryException(other), d(internal::make_unique(*(other.d))) {} //! @endcond // --------------------------------------------------------------------------- /** \brief Returns authority name. */ const std::string &NoSuchAuthorityCodeException::getAuthority() const { return d->authority_; } // --------------------------------------------------------------------------- /** \brief Returns authority code. */ const std::string &NoSuchAuthorityCodeException::getAuthorityCode() const { return d->code_; } // --------------------------------------------------------------------------- } // namespace io NS_PROJ_END proj-6.3.1/src/iso19111/common.cpp0000664000175000017500000012675613601752712013415 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: ISO19111:2019 implementation * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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 FROM_PROJ_CPP #define FROM_PROJ_CPP #endif #include "proj/common.hpp" #include "proj/io.hpp" #include "proj/metadata.hpp" #include "proj/util.hpp" #include "proj/internal/internal.hpp" #include "proj/internal/io_internal.hpp" #include "proj.h" #include // M_PI #include #include #include #include using namespace NS_PROJ::internal; using namespace NS_PROJ::io; using namespace NS_PROJ::metadata; using namespace NS_PROJ::util; #if 0 namespace dropbox{ namespace oxygen { template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; template<> nn::~nn() = default; }} #endif NS_PROJ_START namespace common { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct UnitOfMeasure::Private { std::string name_{}; double toSI_ = 1.0; UnitOfMeasure::Type type_{UnitOfMeasure::Type::UNKNOWN}; std::string codeSpace_{}; std::string code_{}; Private(const std::string &nameIn, double toSIIn, UnitOfMeasure::Type typeIn, const std::string &codeSpaceIn, const std::string &codeIn) : name_(nameIn), toSI_(toSIIn), type_(typeIn), codeSpace_(codeSpaceIn), code_(codeIn) {} }; //! @endcond // --------------------------------------------------------------------------- /** \brief Creates a UnitOfMeasure. */ UnitOfMeasure::UnitOfMeasure(const std::string &nameIn, double toSIIn, UnitOfMeasure::Type typeIn, const std::string &codeSpaceIn, const std::string &codeIn) : d(internal::make_unique(nameIn, toSIIn, typeIn, codeSpaceIn, codeIn)) {} // --------------------------------------------------------------------------- UnitOfMeasure::UnitOfMeasure(const UnitOfMeasure &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress UnitOfMeasure::~UnitOfMeasure() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress UnitOfMeasure &UnitOfMeasure::operator=(const UnitOfMeasure &other) { if (this != &other) { *d = *(other.d); } return *this; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress UnitOfMeasureNNPtr UnitOfMeasure::create(const UnitOfMeasure &other) { return util::nn_make_shared(other); } //! @endcond // --------------------------------------------------------------------------- /** \brief Return the name of the unit of measure. */ const std::string &UnitOfMeasure::name() PROJ_PURE_DEFN { return d->name_; } // --------------------------------------------------------------------------- /** \brief Return the conversion factor to the unit of the * International System of Units of the same Type. * * For example, for foot, this would be 0.3048 (metre) * * @return the conversion factor, or 0 if no conversion exists. */ double UnitOfMeasure::conversionToSI() PROJ_PURE_DEFN { return d->toSI_; } // --------------------------------------------------------------------------- /** \brief Return the type of the unit of measure. */ UnitOfMeasure::Type UnitOfMeasure::type() PROJ_PURE_DEFN { return d->type_; } // --------------------------------------------------------------------------- /** \brief Return the code space of the unit of measure. * * For example "EPSG" * * @return the code space, or empty string. */ const std::string &UnitOfMeasure::codeSpace() PROJ_PURE_DEFN { return d->codeSpace_; } // --------------------------------------------------------------------------- /** \brief Return the code of the unit of measure. * * @return the code, or empty string. */ const std::string &UnitOfMeasure::code() PROJ_PURE_DEFN { return d->code_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void UnitOfMeasure::_exportToWKT( WKTFormatter *formatter, const std::string &unitType) const // throw(FormattingException) { const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; const auto l_type = type(); if (formatter->forceUNITKeyword() && l_type != Type::PARAMETRIC) { formatter->startNode(WKTConstants::UNIT, !codeSpace().empty()); } else if (!unitType.empty()) { formatter->startNode(unitType, !codeSpace().empty()); } else { if (isWKT2 && l_type == Type::LINEAR) { formatter->startNode(WKTConstants::LENGTHUNIT, !codeSpace().empty()); } else if (isWKT2 && l_type == Type::ANGULAR) { formatter->startNode(WKTConstants::ANGLEUNIT, !codeSpace().empty()); } else if (isWKT2 && l_type == Type::SCALE) { formatter->startNode(WKTConstants::SCALEUNIT, !codeSpace().empty()); } else if (isWKT2 && l_type == Type::TIME) { formatter->startNode(WKTConstants::TIMEUNIT, !codeSpace().empty()); } else if (isWKT2 && l_type == Type::PARAMETRIC) { formatter->startNode(WKTConstants::PARAMETRICUNIT, !codeSpace().empty()); } else { formatter->startNode(WKTConstants::UNIT, !codeSpace().empty()); } } { const auto &l_name = name(); const bool esri = formatter->useESRIDialect(); if (esri) { if (ci_equal(l_name, "degree")) { formatter->addQuotedString("Degree"); } else if (ci_equal(l_name, "grad")) { formatter->addQuotedString("Grad"); } else if (ci_equal(l_name, "metre")) { formatter->addQuotedString("Meter"); } else { formatter->addQuotedString(l_name); } } else { formatter->addQuotedString(l_name); } const auto &factor = conversionToSI(); if (!isWKT2 || l_type != Type::TIME || factor != 0.0) { // Some TIMEUNIT do not have a conversion factor formatter->add(factor); } if (!codeSpace().empty() && formatter->outputId()) { formatter->startNode( isWKT2 ? WKTConstants::ID : WKTConstants::AUTHORITY, false); formatter->addQuotedString(codeSpace()); const auto &l_code = code(); if (isWKT2) { try { (void)std::stoi(l_code); formatter->add(l_code); } catch (const std::exception &) { formatter->addQuotedString(l_code); } } else { formatter->addQuotedString(l_code); } formatter->endNode(); } } formatter->endNode(); } // --------------------------------------------------------------------------- void UnitOfMeasure::_exportToJSON( JSONFormatter *formatter) const // throw(FormattingException) { auto &writer = formatter->writer(); const auto &l_codeSpace = codeSpace(); auto objContext( formatter->MakeObjectContext(nullptr, !l_codeSpace.empty())); writer.AddObjKey("type"); const auto l_type = type(); if (l_type == Type::LINEAR) { writer.Add("LinearUnit"); } else if (l_type == Type::ANGULAR) { writer.Add("AngularUnit"); } else if (l_type == Type::SCALE) { writer.Add("ScaleUnit"); } else if (l_type == Type::TIME) { writer.Add("TimeUnit"); } else if (l_type == Type::PARAMETRIC) { writer.Add("ParametricUnit"); } else { writer.Add("Unit"); } writer.AddObjKey("name"); const auto &l_name = name(); writer.Add(l_name); const auto &factor = conversionToSI(); writer.AddObjKey("conversion_factor"); writer.Add(factor, 15); if (!l_codeSpace.empty() && formatter->outputId()) { writer.AddObjKey("id"); auto idContext(formatter->MakeObjectContext(nullptr, false)); writer.AddObjKey("authority"); writer.Add(l_codeSpace); writer.AddObjKey("code"); const auto &l_code = code(); try { writer.Add(std::stoi(l_code)); } catch (const std::exception &) { writer.Add(l_code); } } } //! @endcond // --------------------------------------------------------------------------- /** Returns whether two units of measures are equal. * * The comparison is based on the name. */ bool UnitOfMeasure::operator==(const UnitOfMeasure &other) PROJ_PURE_DEFN { return name() == other.name(); } // --------------------------------------------------------------------------- /** Returns whether two units of measures are different. * * The comparison is based on the name. */ bool UnitOfMeasure::operator!=(const UnitOfMeasure &other) PROJ_PURE_DEFN { return name() != other.name(); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress std::string UnitOfMeasure::exportToPROJString() const { if (type() == Type::LINEAR) { auto proj_units = proj_list_units(); for (int i = 0; proj_units[i].id != nullptr; i++) { if (::fabs(proj_units[i].factor - conversionToSI()) < 1e-10 * conversionToSI()) { return proj_units[i].id; } } } else if (type() == Type::ANGULAR) { auto proj_angular_units = proj_list_angular_units(); for (int i = 0; proj_angular_units[i].id != nullptr; i++) { if (::fabs(proj_angular_units[i].factor - conversionToSI()) < 1e-10 * conversionToSI()) { return proj_angular_units[i].id; } } } return std::string(); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool UnitOfMeasure::_isEquivalentTo( const UnitOfMeasure &other, util::IComparable::Criterion criterion) const { if (criterion == util::IComparable::Criterion::STRICT) { return operator==(other); } return std::fabs(conversionToSI() - other.conversionToSI()) <= 1e-10 * std::fabs(conversionToSI()); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct Measure::Private { double value_ = 0.0; UnitOfMeasure unit_{}; Private(double valueIn, const UnitOfMeasure &unitIn) : value_(valueIn), unit_(unitIn) {} }; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Measure. */ Measure::Measure(double valueIn, const UnitOfMeasure &unitIn) : d(internal::make_unique(valueIn, unitIn)) {} // --------------------------------------------------------------------------- Measure::Measure(const Measure &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Measure::~Measure() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the unit of the Measure. */ const UnitOfMeasure &Measure::unit() PROJ_PURE_DEFN { return d->unit_; } // --------------------------------------------------------------------------- /** \brief Return the value of the Measure, after conversion to the * corresponding * unit of the International System. */ double Measure::getSIValue() PROJ_PURE_DEFN { return d->value_ * d->unit_.conversionToSI(); } // --------------------------------------------------------------------------- /** \brief Return the value of the measure, expressed in the unit() */ double Measure::value() PROJ_PURE_DEFN { return d->value_; } // --------------------------------------------------------------------------- /** \brief Return the value of this measure expressed into the provided unit. */ double Measure::convertToUnit(const UnitOfMeasure &otherUnit) PROJ_PURE_DEFN { return getSIValue() / otherUnit.conversionToSI(); } // --------------------------------------------------------------------------- /** \brief Return whether two measures are equal. * * The comparison is done both on the value and the unit. */ bool Measure::operator==(const Measure &other) PROJ_PURE_DEFN { return d->value_ == other.d->value_ && d->unit_ == other.d->unit_; } // --------------------------------------------------------------------------- /** \brief Returns whether an object is equivalent to another one. * @param other other object to compare to * @param criterion comparaison criterion. * @param maxRelativeError Maximum relative error allowed. * @return true if objects are equivalent. */ bool Measure::_isEquivalentTo(const Measure &other, util::IComparable::Criterion criterion, double maxRelativeError) const { if (criterion == util::IComparable::Criterion::STRICT) { return operator==(other); } return std::fabs(getSIValue() - other.getSIValue()) <= maxRelativeError * std::fabs(getSIValue()); } // --------------------------------------------------------------------------- /** \brief Instantiate a Scale. * * @param valueIn value */ Scale::Scale(double valueIn) : Measure(valueIn, UnitOfMeasure::SCALE_UNITY) {} // --------------------------------------------------------------------------- /** \brief Instantiate a Scale. * * @param valueIn value * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::SCALE */ Scale::Scale(double valueIn, const UnitOfMeasure &unitIn) : Measure(valueIn, unitIn) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Scale::Scale(const Scale &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Scale::~Scale() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Angle. * * @param valueIn value */ Angle::Angle(double valueIn) : Measure(valueIn, UnitOfMeasure::DEGREE) {} // --------------------------------------------------------------------------- /** \brief Instantiate a Angle. * * @param valueIn value * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::ANGULAR */ Angle::Angle(double valueIn, const UnitOfMeasure &unitIn) : Measure(valueIn, unitIn) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Angle::Angle(const Angle &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Angle::~Angle() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a Length. * * @param valueIn value */ Length::Length(double valueIn) : Measure(valueIn, UnitOfMeasure::METRE) {} // --------------------------------------------------------------------------- /** \brief Instantiate a Length. * * @param valueIn value * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::LINEAR */ Length::Length(double valueIn, const UnitOfMeasure &unitIn) : Measure(valueIn, unitIn) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Length::Length(const Length &) = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress Length::~Length() = default; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DateTime::Private { std::string str_{}; explicit Private(const std::string &str) : str_(str) {} }; //! @endcond // --------------------------------------------------------------------------- DateTime::DateTime() : d(internal::make_unique(std::string())) {} // --------------------------------------------------------------------------- DateTime::DateTime(const std::string &str) : d(internal::make_unique(str)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DateTime::DateTime(const DateTime &other) : d(internal::make_unique(*(other.d))) {} //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DateTime &DateTime::operator=(const DateTime &other) { d->str_ = other.d->str_; return *this; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DateTime::~DateTime() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Instantiate a DateTime. */ DateTime DateTime::create(const std::string &str) { return DateTime(str); } // --------------------------------------------------------------------------- /** \brief Return whether the DateTime is ISO:8601 compliant. * * \remark The current implementation is really simplistic, and aimed at * detecting date-times that are not ISO:8601 compliant. */ bool DateTime::isISO_8601() const { return !d->str_.empty() && d->str_[0] >= '0' && d->str_[0] <= '9' && d->str_.find(' ') == std::string::npos; } // --------------------------------------------------------------------------- /** \brief Return the DateTime as a string. */ std::string DateTime::toString() const { return d->str_; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress // cppcheck-suppress copyCtorAndEqOperator struct IdentifiedObject::Private { IdentifierNNPtr name{Identifier::create()}; std::vector identifiers{}; std::vector aliases{}; std::string remarks{}; bool isDeprecated{}; void setIdentifiers(const PropertyMap &properties); void setName(const PropertyMap &properties); void setAliases(const PropertyMap &properties); }; //! @endcond // --------------------------------------------------------------------------- IdentifiedObject::IdentifiedObject() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- IdentifiedObject::IdentifiedObject(const IdentifiedObject &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress IdentifiedObject::~IdentifiedObject() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the name of the object. * * Generally, the only interesting field of the name will be * name()->description(). */ const IdentifierNNPtr &IdentifiedObject::name() PROJ_PURE_DEFN { return d->name; } // --------------------------------------------------------------------------- /** \brief Return the name of the object. * * Return *(name()->description()) */ const std::string &IdentifiedObject::nameStr() PROJ_PURE_DEFN { return *(d->name->description()); } // --------------------------------------------------------------------------- /** \brief Return the identifier(s) of the object * * Generally, those will have Identifier::code() and Identifier::codeSpace() * filled. */ const std::vector & IdentifiedObject::identifiers() PROJ_PURE_DEFN { return d->identifiers; } // --------------------------------------------------------------------------- /** \brief Return the alias(es) of the object. */ const std::vector & IdentifiedObject::aliases() PROJ_PURE_DEFN { return d->aliases; } // --------------------------------------------------------------------------- /** \brief Return the (first) alias of the object as a string. * * Shortcut for aliases()[0]->toFullyQualifiedName()->toString() */ std::string IdentifiedObject::alias() PROJ_PURE_DEFN { if (d->aliases.empty()) return std::string(); return d->aliases[0]->toFullyQualifiedName()->toString(); } // --------------------------------------------------------------------------- /** \brief Return the EPSG code. * @return code, or 0 if not found */ int IdentifiedObject::getEPSGCode() PROJ_PURE_DEFN { for (const auto &id : identifiers()) { if (ci_equal(*(id->codeSpace()), metadata::Identifier::EPSG)) { return ::atoi(id->code().c_str()); } } return 0; } // --------------------------------------------------------------------------- /** \brief Return the remarks. */ const std::string &IdentifiedObject::remarks() PROJ_PURE_DEFN { return d->remarks; } // --------------------------------------------------------------------------- /** \brief Return whether the object is deprecated. * * \remark Extension of \ref ISO_19111_2019 */ bool IdentifiedObject::isDeprecated() PROJ_PURE_DEFN { return d->isDeprecated; } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void IdentifiedObject::Private::setName( const PropertyMap &properties) // throw(InvalidValueTypeException) { const auto pVal = properties.get(NAME_KEY); if (!pVal) { return; } if (const auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == BoxedValue::Type::STRING) { name = Identifier::createFromDescription(genVal->stringValue()); } else { throw InvalidValueTypeException("Invalid value type for " + NAME_KEY); } } else { if (auto identifier = util::nn_dynamic_pointer_cast(*pVal)) { name = NN_NO_CHECK(identifier); } else { throw InvalidValueTypeException("Invalid value type for " + NAME_KEY); } } } // --------------------------------------------------------------------------- void IdentifiedObject::Private::setIdentifiers( const PropertyMap &properties) // throw(InvalidValueTypeException) { auto pVal = properties.get(IDENTIFIERS_KEY); if (!pVal) { pVal = properties.get(Identifier::CODE_KEY); if (pVal) { identifiers.push_back( Identifier::create(std::string(), properties)); } return; } if (auto identifier = util::nn_dynamic_pointer_cast(*pVal)) { identifiers.clear(); identifiers.push_back(NN_NO_CHECK(identifier)); } else { if (auto array = dynamic_cast(pVal->get())) { identifiers.clear(); for (const auto &val : *array) { identifier = util::nn_dynamic_pointer_cast(val); if (identifier) { identifiers.push_back(NN_NO_CHECK(identifier)); } else { throw InvalidValueTypeException("Invalid value type for " + IDENTIFIERS_KEY); } } } else { throw InvalidValueTypeException("Invalid value type for " + IDENTIFIERS_KEY); } } } // --------------------------------------------------------------------------- void IdentifiedObject::Private::setAliases( const PropertyMap &properties) // throw(InvalidValueTypeException) { const auto pVal = properties.get(ALIAS_KEY); if (!pVal) { return; } if (auto l_name = util::nn_dynamic_pointer_cast(*pVal)) { aliases.clear(); aliases.push_back(NN_NO_CHECK(l_name)); } else { if (const auto array = dynamic_cast(pVal->get())) { aliases.clear(); for (const auto &val : *array) { l_name = util::nn_dynamic_pointer_cast(val); if (l_name) { aliases.push_back(NN_NO_CHECK(l_name)); } else { if (auto genVal = dynamic_cast(val.get())) { if (genVal->type() == BoxedValue::Type::STRING) { aliases.push_back(NameFactory::createLocalName( nullptr, genVal->stringValue())); } else { throw InvalidValueTypeException( "Invalid value type for " + ALIAS_KEY); } } else { throw InvalidValueTypeException( "Invalid value type for " + ALIAS_KEY); } } } } else { std::string temp; if (properties.getStringValue(ALIAS_KEY, temp)) { aliases.clear(); aliases.push_back(NameFactory::createLocalName(nullptr, temp)); } else { throw InvalidValueTypeException("Invalid value type for " + ALIAS_KEY); } } } } //! @endcond // --------------------------------------------------------------------------- void IdentifiedObject::setProperties( const PropertyMap &properties) // throw(InvalidValueTypeException) { d->setName(properties); d->setIdentifiers(properties); d->setAliases(properties); properties.getStringValue(REMARKS_KEY, d->remarks); { const auto pVal = properties.get(DEPRECATED_KEY); if (pVal) { if (const auto genVal = dynamic_cast(pVal->get())) { if (genVal->type() == BoxedValue::Type::BOOLEAN) { d->isDeprecated = genVal->booleanValue(); } else { throw InvalidValueTypeException("Invalid value type for " + DEPRECATED_KEY); } } else { throw InvalidValueTypeException("Invalid value type for " + DEPRECATED_KEY); } } } } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void IdentifiedObject::formatID(WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; for (const auto &id : identifiers()) { id->_exportToWKT(formatter); if (!isWKT2) { break; } } } // --------------------------------------------------------------------------- void IdentifiedObject::formatRemarks(WKTFormatter *formatter) const { if (!remarks().empty()) { formatter->startNode(WKTConstants::REMARK, false); formatter->addQuotedString(remarks()); formatter->endNode(); } } // --------------------------------------------------------------------------- void IdentifiedObject::formatID(JSONFormatter *formatter) const { const auto &ids(identifiers()); auto &writer = formatter->writer(); if (ids.size() == 1) { writer.AddObjKey("id"); ids.front()->_exportToJSON(formatter); } else if (!ids.empty()) { writer.AddObjKey("ids"); auto arrayContext(writer.MakeArrayContext()); for (const auto &id : ids) { id->_exportToJSON(formatter); } } } // --------------------------------------------------------------------------- void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const { if (!remarks().empty()) { auto &writer = formatter->writer(); writer.AddObjKey("remarks"); writer.Add(remarks()); } } // --------------------------------------------------------------------------- bool IdentifiedObject::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherIdObj = dynamic_cast(other); if (!otherIdObj) return false; return _isEquivalentTo(otherIdObj, criterion, dbContext); } // --------------------------------------------------------------------------- bool IdentifiedObject::_isEquivalentTo( const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN { if (criterion == util::IComparable::Criterion::STRICT) { if (!ci_equal(nameStr(), otherIdObj->nameStr())) { return false; } // TODO test id etc } else { if (!metadata::Identifier::isEquivalentName( nameStr().c_str(), otherIdObj->nameStr().c_str())) { return hasEquivalentNameToUsingAlias(otherIdObj, dbContext); } } return true; } // --------------------------------------------------------------------------- bool IdentifiedObject::hasEquivalentNameToUsingAlias( const IdentifiedObject *, const io::DatabaseContextPtr &) const { return false; } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ObjectDomain::Private { optional scope_{}; ExtentPtr domainOfValidity_{}; Private(const optional &scopeIn, const ExtentPtr &extent) : scope_(scopeIn), domainOfValidity_(extent) {} }; //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ObjectDomain::ObjectDomain(const optional &scopeIn, const ExtentPtr &extent) : d(internal::make_unique(scopeIn, extent)) {} //! @endcond // --------------------------------------------------------------------------- ObjectDomain::ObjectDomain(const ObjectDomain &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ObjectDomain::~ObjectDomain() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the scope. * * @return the scope, or empty. */ const optional &ObjectDomain::scope() PROJ_PURE_DEFN { return d->scope_; } // --------------------------------------------------------------------------- /** \brief Return the domain of validity. * * @return the domain of validity, or nullptr. */ const ExtentPtr &ObjectDomain::domainOfValidity() PROJ_PURE_DEFN { return d->domainOfValidity_; } // --------------------------------------------------------------------------- /** \brief Instantiate a ObjectDomain. */ ObjectDomainNNPtr ObjectDomain::create(const optional &scopeIn, const ExtentPtr &extent) { return ObjectDomain::nn_make_shared(scopeIn, extent); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ObjectDomain::_exportToWKT(WKTFormatter *formatter) const { if (d->scope_.has_value()) { formatter->startNode(WKTConstants::SCOPE, false); formatter->addQuotedString(*(d->scope_)); formatter->endNode(); } else if (formatter->use2019Keywords()) { formatter->startNode(WKTConstants::SCOPE, false); formatter->addQuotedString("unknown"); formatter->endNode(); } if (d->domainOfValidity_) { if (d->domainOfValidity_->description().has_value()) { formatter->startNode(WKTConstants::AREA, false); formatter->addQuotedString(*(d->domainOfValidity_->description())); formatter->endNode(); } if (d->domainOfValidity_->geographicElements().size() == 1) { const auto bbox = dynamic_cast( d->domainOfValidity_->geographicElements()[0].get()); if (bbox) { formatter->startNode(WKTConstants::BBOX, false); formatter->add(bbox->southBoundLatitude()); formatter->add(bbox->westBoundLongitude()); formatter->add(bbox->northBoundLatitude()); formatter->add(bbox->eastBoundLongitude()); formatter->endNode(); } } if (d->domainOfValidity_->verticalElements().size() == 1) { auto extent = d->domainOfValidity_->verticalElements()[0]; formatter->startNode(WKTConstants::VERTICALEXTENT, false); formatter->add(extent->minimumValue()); formatter->add(extent->maximumValue()); extent->unit()->_exportToWKT(formatter); formatter->endNode(); } if (d->domainOfValidity_->temporalElements().size() == 1) { auto extent = d->domainOfValidity_->temporalElements()[0]; formatter->startNode(WKTConstants::TIMEEXTENT, false); if (DateTime::create(extent->start()).isISO_8601()) { formatter->add(extent->start()); } else { formatter->addQuotedString(extent->start()); } if (DateTime::create(extent->stop()).isISO_8601()) { formatter->add(extent->stop()); } else { formatter->addQuotedString(extent->stop()); } formatter->endNode(); } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const { auto &writer = formatter->writer(); if (d->scope_.has_value()) { writer.AddObjKey("scope"); writer.Add(*(d->scope_)); } if (d->domainOfValidity_) { if (d->domainOfValidity_->description().has_value()) { writer.AddObjKey("area"); writer.Add(*(d->domainOfValidity_->description())); } if (d->domainOfValidity_->geographicElements().size() == 1) { const auto bbox = dynamic_cast( d->domainOfValidity_->geographicElements()[0].get()); if (bbox) { writer.AddObjKey("bbox"); auto bboxContext(writer.MakeObjectContext()); writer.AddObjKey("south_latitude"); writer.Add(bbox->southBoundLatitude(), 15); writer.AddObjKey("west_longitude"); writer.Add(bbox->westBoundLongitude(), 15); writer.AddObjKey("north_latitude"); writer.Add(bbox->northBoundLatitude(), 15); writer.AddObjKey("east_longitude"); writer.Add(bbox->eastBoundLongitude(), 15); } } if (d->domainOfValidity_->verticalElements().size() == 1) { // TODO } if (d->domainOfValidity_->temporalElements().size() == 1) { // TODO } } } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool ObjectDomain::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherDomain = dynamic_cast(other); if (!otherDomain) return false; if (scope().has_value() != otherDomain->scope().has_value()) return false; if (*scope() != *otherDomain->scope()) return false; if ((domainOfValidity().get() != nullptr) ^ (otherDomain->domainOfValidity().get() != nullptr)) return false; return domainOfValidity().get() == nullptr || domainOfValidity()->_isEquivalentTo( otherDomain->domainOfValidity().get(), criterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct ObjectUsage::Private { std::vector domains_{}; }; //! @endcond // --------------------------------------------------------------------------- ObjectUsage::ObjectUsage() : d(internal::make_unique()) {} // --------------------------------------------------------------------------- ObjectUsage::ObjectUsage(const ObjectUsage &other) : IdentifiedObject(other), d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress ObjectUsage::~ObjectUsage() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the domains of the object. */ const std::vector &ObjectUsage::domains() PROJ_PURE_DEFN { return d->domains_; } // --------------------------------------------------------------------------- void ObjectUsage::setProperties( const PropertyMap &properties) // throw(InvalidValueTypeException) { IdentifiedObject::setProperties(properties); optional scope; properties.getStringValue(SCOPE_KEY, scope); ExtentPtr domainOfValidity; { const auto pVal = properties.get(DOMAIN_OF_VALIDITY_KEY); if (pVal) { domainOfValidity = util::nn_dynamic_pointer_cast(*pVal); if (!domainOfValidity) { throw InvalidValueTypeException("Invalid value type for " + DOMAIN_OF_VALIDITY_KEY); } } } if (scope.has_value() || domainOfValidity) { d->domains_.emplace_back(ObjectDomain::create(scope, domainOfValidity)); } { const auto pVal = properties.get(OBJECT_DOMAIN_KEY); if (pVal) { if (auto objectDomain = util::nn_dynamic_pointer_cast(*pVal)) { d->domains_.emplace_back(NN_NO_CHECK(objectDomain)); } else if (const auto array = dynamic_cast( pVal->get())) { for (const auto &val : *array) { objectDomain = util::nn_dynamic_pointer_cast(val); if (objectDomain) { d->domains_.emplace_back(NN_NO_CHECK(objectDomain)); } else { throw InvalidValueTypeException( "Invalid value type for " + OBJECT_DOMAIN_KEY); } } } else { throw InvalidValueTypeException("Invalid value type for " + OBJECT_DOMAIN_KEY); } } } } // --------------------------------------------------------------------------- void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const { const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; if (isWKT2 && formatter->outputUsage()) { auto l_domains = domains(); if (!l_domains.empty()) { if (formatter->use2019Keywords()) { for (const auto &domain : l_domains) { formatter->startNode(WKTConstants::USAGE, false); domain->_exportToWKT(formatter); formatter->endNode(); } } else { l_domains[0]->_exportToWKT(formatter); } } } if (formatter->outputId()) { formatID(formatter); } if (isWKT2) { formatRemarks(formatter); } } // --------------------------------------------------------------------------- void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const { auto &writer = formatter->writer(); if (formatter->outputUsage()) { const auto &l_domains = domains(); if (l_domains.size() == 1) { l_domains[0]->_exportToJSON(formatter); } else if (!l_domains.empty()) { writer.AddObjKey("usages"); auto arrayContext(writer.MakeArrayContext(false)); for (const auto &domain : l_domains) { auto objContext(writer.MakeObjectContext()); domain->_exportToJSON(formatter); } } } if (formatter->outputId()) { formatID(formatter); } formatRemarks(formatter); } // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress bool ObjectUsage::_isEquivalentTo( const util::IComparable *other, util::IComparable::Criterion criterion, const io::DatabaseContextPtr &dbContext) const { auto otherObjUsage = dynamic_cast(other); if (!otherObjUsage) return false; // TODO: incomplete return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext); } //! @endcond // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress struct DataEpoch::Private { Measure coordinateEpoch_{}; explicit Private(const Measure &coordinateEpochIn) : coordinateEpoch_(coordinateEpochIn) {} }; //! @endcond // --------------------------------------------------------------------------- DataEpoch::DataEpoch() : d(internal::make_unique(Measure())) {} // --------------------------------------------------------------------------- DataEpoch::DataEpoch(const Measure &coordinateEpochIn) : d(internal::make_unique(coordinateEpochIn)) {} // --------------------------------------------------------------------------- DataEpoch::DataEpoch(const DataEpoch &other) : d(internal::make_unique(*(other.d))) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress DataEpoch::~DataEpoch() = default; //! @endcond // --------------------------------------------------------------------------- /** \brief Return the coordinate epoch, as a measure in decimal year. */ const Measure &DataEpoch::coordinateEpoch() const { return d->coordinateEpoch_; } } // namespace common NS_PROJ_END proj-6.3.1/src/jniproj.cpp0000664000175000017500000003725213601752712012401 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Java/JNI wrappers for PROJ API. * Author: Antonello Andrea * Martin Desruisseaux * ****************************************************************************** * Copyright (c) 2005, Andrea Antonello * Copyright (c) 2011, Martin Desruisseaux * Copyright (c) 2018, 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. *****************************************************************************/ /*! * \file jniproj.c * * \brief * Functions used by the Java Native Interface (JNI) wrappers of PROJ. * * * \author Antonello Andrea * \date Wed Oct 20 23:10:24 CEST 2004 * * \author Martin Desruisseaux * \date August 2011 */ #include "proj_config.h" #ifdef JNI_ENABLED #include #include #include "proj_internal.h" #include "org_proj4_PJ.h" #include #define PJ_FIELD_NAME "ptr" #define PJ_FIELD_TYPE "J" /*! * \brief * Internal method returning the address of the PJ structure wrapped by the given Java object. * This function looks for a field named "ptr" and of type "long" (Java signature "J") in the * given object. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The address of the PJ structure, or nullptr if the operation fails (for example * because the "ptr" field was not found). */ static PJ *getPJ(JNIEnv *env, jobject object) { jfieldID id = env->GetFieldID(env->GetObjectClass(object), PJ_FIELD_NAME, PJ_FIELD_TYPE); return (id) ? (PJ*) env->GetLongField(object, id) : nullptr; } /*! * \brief * Internal method returning the java.lang.Double.NaN constant value. * Efficiency is no a high concern for this particular method, because it * is used mostly when the user wrongly attempt to use a disposed PJ object. * * \param env - The JNI environment. * \return The java.lang.Double.NaN constant value. */ static jdouble javaNaN(JNIEnv *env) { jclass c = env->FindClass("java/lang/Double"); if (c) { // Should never be nullptr, but let be paranoiac. jfieldID id = env->GetStaticFieldID(c, "NaN", "D"); if (id) { // Should never be nullptr, but let be paranoiac. return env->GetStaticDoubleField(c, id); } } return 0.0; // Should never happen. } /*! * \brief * Returns the Proj4 release number. * * \param env - The JNI environment. * \return The Proj4 release number, or nullptr. */ JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion (JNIEnv *env, jclass) { const char *desc = pj_get_release(); return (desc) ? env->NewStringUTF(desc) : nullptr; } /*! * \brief * Allocates a new PJ structure from a definition string. * * \param env - The JNI environment. * \param definition - The string definition to be given to Proj4. * \return The address of the new PJ structure, or 0 in case of failure. */ JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ (JNIEnv *env, jclass, jstring definition) { const char *def_utf = env->GetStringUTFChars(definition, nullptr); if (!def_utf) return 0; /* OutOfMemoryError already thrown. */ PJ *pj = pj_init_plus(def_utf); env->ReleaseStringUTFChars(definition, def_utf); return (jlong) pj; } /*! * \brief * Allocates a new geographic PJ structure from an existing one. * * \param env - The JNI environment. * \param projected - The PJ object from which to derive a new one. * \return The address of the new PJ structure, or 0 in case of failure. */ JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ (JNIEnv *env, jclass, jobject projected) { PJ *pj = getPJ(env, projected); return (pj) ? (jlong) pj_latlong_from_proj(pj) : 0; } /*! * \brief * Returns the definition string. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The definition string. */ JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (pj) { char *desc = pj_get_def(pj, 0); if (desc) { jstring str = env->NewStringUTF(desc); pj_dalloc(desc); return str; } } return nullptr; } /*! * \brief * Returns the description associated to the PJ structure. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The description associated to the PJ structure. */ JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (pj) { const char *desc = pj->descr; if (desc) { return env->NewStringUTF(desc); } } return nullptr; } /*! * \brief * Returns the CRS type as one of the PJ.Type enum: GEOGRAPHIC, GEOCENTRIC or PROJECTED. * This function should never return nullptr, unless class or fields have been renamed in * such a way that we can not find anymore the expected enum values. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The CRS type as one of the PJ.Type enum. */ JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (pj) { const char *type; if (pj_is_latlong(pj)) { type = "GEOGRAPHIC"; } else if (pj_is_geocent(pj)) { type = "GEOCENTRIC"; } else { type = "PROJECTED"; } jclass c = env->FindClass("org/proj4/PJ$Type"); if (c) { jfieldID id = env->GetStaticFieldID(c, type, "Lorg/proj4/PJ$Type;"); if (id) { return env->GetStaticObjectField(c, id); } } } return nullptr; } /*! * \brief * Returns the semi-major axis length. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The semi-major axis length. */ JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); return pj ? pj->a_orig : javaNaN(env); } /*! * \brief * Computes the semi-minor axis length from the semi-major axis length and the eccentricity * squared. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The semi-minor axis length. */ JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (!pj) return javaNaN(env); double a = pj->a_orig; return sqrt(a*a * (1.0 - pj->es_orig)); } /*! * \brief * Returns the eccentricity squared. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The eccentricity. */ JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); return pj ? pj->es_orig : javaNaN(env); } /*! * \brief * Returns an array of character indicating the direction of each axis. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The axis directions. */ JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (pj) { int length = static_cast(strlen(pj->axis)); jcharArray array = env->NewCharArray(length); if (array) { jchar* axis = env->GetCharArrayElements(array, nullptr); if (axis) { /* Don't use memcp because the type may not be the same. */ int i; for (i=0; iaxis[i]; } env->ReleaseCharArrayElements(array, axis, 0); } return array; } } return nullptr; } /*! * \brief * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The prime meridian longitude, in degrees. */ JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); return (pj) ? (pj->from_greenwich)*(180/M_PI) : javaNaN(env); } /*! * \brief * Returns the conversion factor from linear units to metres. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \param vertical - JNI_FALSE for horizontal axes, or JNI_TRUE for the vertical axis. * \return The conversion factor to metres. */ JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre (JNIEnv *env, jobject object, jboolean vertical) { PJ *pj = getPJ(env, object); if (pj) { return (vertical) ? pj->vto_meter : pj->to_meter; } return javaNaN(env); } /*! * \brief * Converts input values from degrees to radians before coordinate operation, or the output * values from radians to degrees after the coordinate operation. * * \param pj - The PROJ.4 PJ structure. * \param data - The coordinate array to transform. * \param numPts - Number of points to transform. * \param dimension - Dimension of points in the coordinate array. * \param factor - The scale factor to apply: M_PI/180 for inputs or 180/M_PI for outputs. */ static void convertAngularOrdinates(PJ *pj, double* data, jint numPts, int dimension, double factor) { int dimToSkip; if (pj_is_latlong(pj)) { /* Convert only the 2 first ordinates and skip all the other dimensions. */ dimToSkip = dimension - 2; } else { /* Not a geographic CRS: nothing to convert. */ return; } double *stop = data + dimension*numPts; if (dimToSkip > 0) { while (data != stop) { (*data++) *= factor; (*data++) *= factor; data += dimToSkip; } } else { while (data != stop) { (*data++) *= factor; } } } /*! * \brief * Transforms in-place the coordinates in the given array. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \param target - The target CRS. * \param dimension - The dimension of each coordinate value. Must be equals or greater than 2. * \param coordinates - The coordinates to transform, as a sequence of (x,y,,...) tuples. * \param offset - Offset of the first coordinate in the given array. * \param numPts - Number of points to transform. */ JNIEXPORT void JNICALL Java_org_proj4_PJ_transform (JNIEnv *env, jobject object, jobject target, jint dimension, jdoubleArray coordinates, jint offset, jint numPts) { if (!target || !coordinates) { jclass c = env->FindClass("java/lang/NullPointerException"); if (c) env->ThrowNew(c, "The target CRS and the coordinates array can not be null."); return; } if (dimension < 2 || dimension > org_proj4_PJ_DIMENSION_MAX) { /* Arbitrary upper value for catching potential misuse. */ jclass c = env->FindClass("java/lang/IllegalArgumentException"); if (c) env->ThrowNew(c, "Illegal number of dimensions."); return; } if ((offset < 0) || (numPts < 0) || (offset + dimension*numPts) > env->GetArrayLength(coordinates)) { jclass c = env->FindClass("java/lang/ArrayIndexOutOfBoundsException"); if (c) env->ThrowNew(c, "Illegal offset or illegal number of points."); return; } PJ *src_pj = getPJ(env, object); PJ *dst_pj = getPJ(env, target); if (src_pj && dst_pj) { /* Using GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical rather than GetDoubleArrayElements/ReleaseDoubleArrayElements increase the chances that the JVM returns direct reference to its internal array without copying data. However we must promise to run the "critical" code fast, to not make any system call that may wait for the JVM and to not invoke any other JNI method. */ double *data = static_cast(env->GetPrimitiveArrayCritical(coordinates, nullptr)); if (data) { double *x = data + offset; double *y = x + 1; double *z = (dimension >= 3) ? y+1 : nullptr; convertAngularOrdinates(src_pj, x, numPts, dimension, M_PI/180); int err = pj_transform(src_pj, dst_pj, numPts, dimension, x, y, z); convertAngularOrdinates(dst_pj, x, numPts, dimension, 180/M_PI); env->ReleasePrimitiveArrayCritical(coordinates, data, 0); if (err) { jclass c = env->FindClass("org/proj4/PJException"); if (c) env->ThrowNew(c, pj_strerrno(err)); } } } } /*! * \brief * Returns a description of the last error that occurred, or nullptr if none. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). * \return The last error, or nullptr. */ JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError (JNIEnv *env, jobject object) { PJ *pj = getPJ(env, object); if (pj) { int err = pj_ctx_get_errno(pj->ctx); if (err) { return env->NewStringUTF(pj_strerrno(err)); } } return nullptr; } /*! * \brief * Deallocate the PJ structure. This method is invoked by the garbage collector exactly once. * This method will also set the Java "ptr" final field to 0 as a safety. In theory we are not * supposed to change the value of a final field. But no Java code should use this field, and * the PJ object is being garbage collected anyway. We set the field to 0 as a safety in case * some user invoked the finalize() method explicitly despite our warning in the Javadoc to * never do such thing. * * \param env - The JNI environment. * \param object - The Java object wrapping the PJ structure (not allowed to be nullptr). */ JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize (JNIEnv *env, jobject object) { jfieldID id = env->GetFieldID(env->GetObjectClass(object), PJ_FIELD_NAME, PJ_FIELD_TYPE); if (id) { PJ *pj = (PJ*) env->GetLongField(object, id); if (pj) { env->SetLongField(object, id, (jlong) 0); pj_free(pj); } } } #endif proj-6.3.1/src/initcache.cpp0000664000175000017500000001344313601752712012651 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: init file definition cache. * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 2009, 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 #include "proj.h" #include "proj_internal.h" static int cache_count = 0; static int cache_alloc = 0; static char **cache_key = nullptr; static paralist **cache_paralist = nullptr; /************************************************************************/ /* pj_clone_paralist() */ /* */ /* Allocate a copy of a parameter list. */ /************************************************************************/ paralist *pj_clone_paralist( const paralist *list) { paralist *list_copy = nullptr, *next_copy = nullptr; for( ; list != nullptr; list = list->next ) { paralist *newitem = (paralist *) pj_malloc(sizeof(paralist) + strlen(list->param)); newitem->used = 0; newitem->next = nullptr; strcpy( newitem->param, list->param ); if( list_copy == nullptr ) list_copy = newitem; else next_copy->next = newitem; next_copy = newitem; } return list_copy; } /************************************************************************/ /* pj_clear_initcache() */ /* */ /* Clear out all memory held in the init file cache. */ /************************************************************************/ void pj_clear_initcache() { if( cache_alloc > 0 ) { int i; pj_acquire_lock(); for( i = 0; i < cache_count; i++ ) { paralist *n, *t = cache_paralist[i]; pj_dalloc( cache_key[i] ); /* free parameter list elements */ for (; t != nullptr; t = n) { n = t->next; pj_dalloc(t); } } pj_dalloc( cache_key ); pj_dalloc( cache_paralist ); cache_count = 0; cache_alloc= 0; cache_key = nullptr; cache_paralist = nullptr; pj_release_lock(); } } /************************************************************************/ /* pj_search_initcache() */ /* */ /* Search for a matching definition in the init cache. */ /************************************************************************/ paralist *pj_search_initcache( const char *filekey ) { int i; paralist *result = nullptr; pj_acquire_lock(); for( i = 0; result == nullptr && i < cache_count; i++) { if( strcmp(filekey,cache_key[i]) == 0 ) { result = pj_clone_paralist( cache_paralist[i] ); } } pj_release_lock(); return result; } /************************************************************************/ /* pj_insert_initcache() */ /* */ /* Insert a paralist definition in the init file cache. */ /************************************************************************/ void pj_insert_initcache( const char *filekey, const paralist *list ) { pj_acquire_lock(); /* ** Grow list if required. */ if( cache_count == cache_alloc ) { char **cache_key_new; paralist **cache_paralist_new; cache_alloc = cache_alloc * 2 + 15; cache_key_new = (char **) pj_malloc(sizeof(char*) * cache_alloc); if( cache_key && cache_count ) { memcpy( cache_key_new, cache_key, sizeof(char*) * cache_count); } pj_dalloc( cache_key ); cache_key = cache_key_new; cache_paralist_new = (paralist **) pj_malloc(sizeof(paralist*) * cache_alloc); if( cache_paralist && cache_count ) { memcpy( cache_paralist_new, cache_paralist, sizeof(paralist*) * cache_count ); } pj_dalloc( cache_paralist ); cache_paralist = cache_paralist_new; } /* ** Duplicate the filekey and paralist, and insert in cache. */ cache_key[cache_count] = (char *) pj_malloc(strlen(filekey)+1); strcpy( cache_key[cache_count], filekey ); cache_paralist[cache_count] = pj_clone_paralist( list ); cache_count++; pj_release_lock(); } proj-6.3.1/src/init.cpp0000664000175000017500000006763713601752712011703 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Initialize projection object from string definition. Includes * pj_init(), pj_init_plus() and pj_free() function. * Author: Gerald Evenden, Frank Warmerdam * ****************************************************************************** * Copyright (c) 1995, Gerald Evenden * Copyright (c) 2002, 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. *****************************************************************************/ #define PJ_LIB__ #include #include #include #include #include #include #include "geodesic.h" #include "proj.h" #include "proj_internal.h" #include /**************************************************************************************/ static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) { /*************************************************************************************** Convert a string (presumably originating from get_init_string) to a paralist. ***************************************************************************************/ const char *c = definition; paralist *first = nullptr, *next = nullptr; while (*c) { /* Keep a handle to the start of the list, so we have something to return */ if (nullptr==first) first = next = pj_mkparam_ws (c, &c); else next = next->next = pj_mkparam_ws (c, &c); if (nullptr==next) { pj_dealloc_params (ctx, first, ENOMEM); return nullptr; } } if( next == nullptr ) return nullptr; /* Terminate list and return */ next->next = nullptr; return first; } /**************************************************************************************/ static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { /*************************************************************************************** Read a section of an init file. Return its contents as a plain character string. It is the duty of the caller to free the memory allocated for the string. ***************************************************************************************/ #define MAX_LINE_LENGTH 1000 size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1); char *fname, *section; const char *key; char *buffer = nullptr; char *line = nullptr; PAFile fid; size_t n; line = static_cast(pj_malloc (MAX_LINE_LENGTH + 1)); if (nullptr==line) return nullptr; fname = static_cast(pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3)); if (nullptr==fname) { pj_dealloc (line); return nullptr; } /* Support "init=file:section", "+init=file:section", and "file:section" format */ key = strstr (name, "init="); if (nullptr==key) key = name; else key += 5; if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) { pj_dealloc (fname); pj_dealloc (line); return nullptr; } memmove (fname, key, strlen (key) + 1); /* Locate the name of the section we search for */ section = strrchr(fname, ':'); if (nullptr==section) { proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING); pj_dealloc (fname); pj_dealloc (line); return nullptr; } *section = 0; section++; n = strlen (section); pj_log (ctx, PJ_LOG_TRACE, "get_init_string: searching for section [%s] in init file [%s]", section, fname); fid = pj_open_lib (ctx, fname, "rt"); if (nullptr==fid) { pj_dealloc (fname); pj_dealloc (line); proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); return nullptr; } /* Search for section in init file */ for (;;) { /* End of file? */ if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) { pj_dealloc (buffer); pj_dealloc (fname); pj_dealloc (line); pj_ctx_fclose (ctx, fid); proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); return nullptr; } /* At start of right section? */ pj_chomp (line); if ('<'!=line[0]) continue; if (strlen (line) < n + 2) continue; if (line[n + 1] != '>') continue; if (0==strncmp (line + 1, section, n)) break; } /* We're at the first line of the right section - copy line to buffer */ buffer = static_cast(pj_malloc (current_buffer_size)); if (nullptr==buffer) { pj_dealloc (fname); pj_dealloc (line); pj_ctx_fclose (ctx, fid); return nullptr; } /* Skip the "
" indicator, and copy the rest of the line over */ strcpy (buffer, line + strlen (section) + 2); /* Copy the remaining lines of the section to buffer */ for (;;) { char *end_i_cator; size_t next_length, buffer_length; /* Did the section end somewhere in the most recently read line? */ end_i_cator = strchr (buffer, '<'); if (end_i_cator) { *end_i_cator = 0; break; } /* End of file? - done! */ if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) break; /* Otherwise, handle the line. It MAY be the start of the next section, */ /* but that will be handled at the start of next trip through the loop */ buffer_length = strlen (buffer); pj_chomp (line); /* Remove '#' style comments */ next_length = strlen (line) + buffer_length + 2; if (next_length > current_buffer_size) { char *b = static_cast(pj_malloc (2 * current_buffer_size)); if (nullptr==b) { pj_dealloc (buffer); buffer = nullptr; break; } strcpy (b, buffer); current_buffer_size *= 2; pj_dealloc (buffer); buffer = b; } buffer[buffer_length] = ' '; strcpy (buffer + buffer_length + 1, line); } pj_ctx_fclose (ctx, fid); pj_dealloc (fname); pj_dealloc (line); if (nullptr==buffer) return nullptr; pj_shrink (buffer); pj_log (ctx, PJ_LOG_TRACE, "key=%s, value: [%s]", key, buffer); return buffer; } /************************************************************************/ static paralist *get_init(PJ_CONTEXT *ctx, const char *key, int allow_init_epsg) { /************************************************************************* Expand key from buffer or (if not in buffer) from init file *************************************************************************/ const char *xkey; char *definition = nullptr; paralist *init_items = nullptr; if( !ctx ) { ctx = pj_get_default_ctx(); } /* support "init=file:section", "+init=file:section", and "file:section" format */ xkey = strstr (key, "init="); if (nullptr==xkey) xkey = key; else xkey += 5; pj_log (ctx, PJ_LOG_TRACE, "get_init: searching cache for key: [%s]", xkey); /* Is file/key pair already in cache? */ init_items = pj_search_initcache (xkey); if (init_items) return init_items; if( (strncmp(xkey, "epsg:", 5) == 0 || strncmp(xkey, "IGNF:", 5) == 0) ) { char unused[256]; char initname[5]; int exists; memcpy(initname, xkey, 4); initname[4] = 0; if( strncmp(xkey, "epsg:", 5) == 0 ) { exists = ctx->epsg_file_exists; if( exists < 0 ) { exists = pj_find_file(ctx, initname, unused, sizeof(unused)); ctx->epsg_file_exists = exists; } } else { exists = pj_find_file(ctx, initname, unused, sizeof(unused)); } if( !exists ) { char szInitStr[7 + 64]; PJ* src; const char* proj_string; pj_ctx_set_errno( ctx, 0 ); if( !allow_init_epsg ) { pj_log (ctx, PJ_LOG_TRACE, "%s expansion disallowed", xkey); return nullptr; } if( strlen(xkey) > 64 ) { return nullptr; } strcpy(szInitStr, "+init="); strcat(szInitStr, xkey); auto old_proj4_init_rules = ctx->use_proj4_init_rules; ctx->use_proj4_init_rules = true; src = proj_create(ctx, szInitStr); ctx->use_proj4_init_rules = old_proj4_init_rules; if( !src ) { return nullptr; } proj_string = proj_as_proj_string(ctx, src, PJ_PROJ_4, nullptr); if( !proj_string ) { proj_destroy(src); return nullptr; } definition = (char*)calloc(1, strlen(proj_string)+1); if( definition ) { strcpy(definition, proj_string); } proj_destroy(src); } } if( !definition ) { /* If not, we must read it from file */ pj_log (ctx, PJ_LOG_TRACE, "get_init: searching on in init files for [%s]", xkey); definition = get_init_string (ctx, xkey); } if (nullptr==definition) return nullptr; init_items = string_to_paralist (ctx, definition); if (init_items) pj_log (ctx, PJ_LOG_TRACE, "get_init: got [%s], paralist[0,1]: [%s,%s]", definition, init_items->param, init_items->next ? init_items->next->param : "(empty)"); pj_dealloc (definition); if (nullptr==init_items) return nullptr; /* We found it in file - now insert into the cache, before returning */ pj_insert_initcache (xkey, init_items); return init_items; } static void append_default_ellipsoid_to_paralist (paralist *start) { if (nullptr==start) return; /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */ /* or implicitly, because we are initializing a pipeline) */ if (pj_param_exists (start, "no_defs")) return; auto proj = pj_param_exists (start, "proj"); if (nullptr==proj) return; if (strlen (proj->param) < 6) return; if (0==strcmp ("pipeline", proj->param + 5)) return; /* Don't default ellipse if datum, ellps or any ellipsoid information is set */ if (pj_param_exists (start, "datum")) return; if (pj_param_exists (start, "ellps")) return; if (pj_param_exists (start, "a")) return; if (pj_param_exists (start, "b")) return; if (pj_param_exists (start, "rf")) return; if (pj_param_exists (start, "f")) return; if (pj_param_exists (start, "e")) return; if (pj_param_exists (start, "es")) return; /* Locate end of start-list */ paralist *last = nullptr; for (last = start; last->next; last = last->next); /* If we're here, it's OK to append the current default item */ last->next = pj_mkparam("ellps=GRS80"); } /*****************************************************************************/ static paralist *pj_expand_init_internal(PJ_CONTEXT *ctx, paralist *init, int allow_init_epsg) { /****************************************************************************** Append expansion of to the paralist . The expansion is appended, rather than inserted at 's place, since may contain overrides to the expansion. These must take precedence, and hence come first in the expanded list. Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid. The expression 'init=foo:bar ellps=intl' will then expand to: 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80', where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, turning the expansion into an UTM projection on the Hayford ellipsoid. Note that 'init=foo:bar' stays in the list. It is ignored after expansion. ******************************************************************************/ paralist *last; paralist *expn; /* Nowhere to start? */ if (nullptr==init) return nullptr; expn = get_init(ctx, init->param, allow_init_epsg); /* Nothing in expansion? */ if (nullptr==expn) return nullptr; /* Locate the end of the list */ for (last = init; last && last->next; last = last->next); /* Then append and return */ last->next = expn; return init; } paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) { return pj_expand_init_internal(ctx, init, TRUE); } /************************************************************************/ /* pj_init_plus() */ /* */ /* Same as pj_init() except it takes one argument string with */ /* individual arguments preceded by '+', such as "+proj=utm */ /* +zone=11 +ellps=WGS84". */ /************************************************************************/ PJ * pj_init_plus( const char *definition ) { return pj_init_plus_ctx( pj_get_default_ctx(), definition ); } PJ * pj_init_plus_ctx( projCtx ctx, const char *definition ) { #define MAX_ARG 200 char *argv[MAX_ARG]; char *defn_copy; int argc = 0, i, blank_count = 0; PJ *result = nullptr; /* make a copy that we can manipulate */ defn_copy = (char *) pj_malloc( strlen(definition)+1 ); if (!defn_copy) return nullptr; strcpy( defn_copy, definition ); /* split into arguments based on '+' and trim white space */ for( i = 0; defn_copy[i] != '\0'; i++ ) { switch( defn_copy[i] ) { case '+': if( i == 0 || defn_copy[i-1] == '\0' || blank_count > 0 ) { /* trim trailing spaces from the previous param */ if( blank_count > 0 ) { defn_copy[i - blank_count] = '\0'; blank_count = 0; } if( argc+1 == MAX_ARG ) { pj_dalloc( defn_copy ); pj_ctx_set_errno( ctx, PJD_ERR_UNPARSEABLE_CS_DEF ); return nullptr; } argv[argc++] = defn_copy + i + 1; } break; case ' ': case '\t': case '\n': /* trim leading spaces from the current param */ if( i == 0 || defn_copy[i-1] == '\0' || argc == 0 || argv[argc-1] == defn_copy + i ) defn_copy[i] = '\0'; else blank_count++; break; default: /* reset blank_count */ blank_count = 0; } } /* trim trailing spaces from the last param */ defn_copy[i - blank_count] = '\0'; /* perform actual initialization */ result = pj_init_ctx( ctx, argc, argv ); pj_dalloc( defn_copy ); return result; } /************************************************************************/ /* pj_init() */ /* */ /* Main entry point for initialing a PJ projections */ /* definition. Note that the projection specific function is */ /* called to do the initial allocation so it can be created */ /* large enough to hold projection specific parameters. */ /************************************************************************/ PJ * pj_init(int argc, char **argv) { return pj_init_ctx( pj_get_default_ctx(), argc, argv ); } static PJ_CONSTRUCTOR locate_constructor (const char *name) { int i; const char *s; const PJ_OPERATIONS *operations; operations = proj_list_operations(); for (i = 0; (s = operations[i].id) && strcmp(name, s) ; ++i) ; if (nullptr==s) return nullptr; return (PJ_CONSTRUCTOR) operations[i].proj; } PJ * pj_init_ctx(projCtx ctx, int argc, char **argv) { /* Legacy interface: allow init=epsg:XXXX syntax by default */ int allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, TRUE); return pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg); } PJ * pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_init_epsg) { const char *s; char *name; PJ_CONSTRUCTOR proj; paralist *curr, *init, *start; int i; int err; PJ *PIN = nullptr; int n_pipelines = 0; int n_inits = 0; const PJ_UNITS *units; const PJ_PRIME_MERIDIANS *prime_meridians; if (nullptr==ctx) ctx = pj_get_default_ctx (); ctx->last_errno = 0; if (argc <= 0) { pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); return nullptr; } /* count occurrences of pipelines and inits */ for (i = 0; i < argc; ++i) { if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") ) n_pipelines++; if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5)) n_inits++; } /* can't have nested pipelines directly */ if (n_pipelines > 1) { pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); return nullptr; } /* don't allow more than one +init in non-pipeline operations */ if (n_pipelines == 0 && n_inits > 1) { pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS); return nullptr; } /* put arguments into internal linked list */ start = curr = pj_mkparam(argv[0]); if (!curr) { pj_dealloc_params (ctx, start, ENOMEM); return nullptr; } for (i = 1; i < argc; ++i) { curr->next = pj_mkparam(argv[i]); if (!curr->next) { pj_dealloc_params (ctx, start, ENOMEM); return nullptr; } curr = curr->next; } /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */ /* expanded in the individual pipeline steps during pipeline initialization. */ /* Potentially this leads to many nested pipelines, which shouldn't be a */ /* problem when '+init's are expanded as late as possible. */ init = pj_param_exists (start, "init"); if (init && n_pipelines == 0) { init = pj_expand_init_internal (ctx, init, allow_init_epsg); if (!init) { pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS); return nullptr; } } if (ctx->last_errno) { pj_dealloc_params (ctx, start, ctx->last_errno); return nullptr; } /* Find projection selection */ curr = pj_param_exists (start, "proj"); if (nullptr==curr) { pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); return nullptr; } name = curr->param; if (strlen (name) < 6) { pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); return nullptr; } name += 5; proj = locate_constructor (name); if (nullptr==proj) { pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); return nullptr; } append_default_ellipsoid_to_paralist (start); /* Allocate projection structure */ PIN = proj(nullptr); if (nullptr==PIN) { pj_dealloc_params (ctx, start, ENOMEM); return nullptr; } PIN->ctx = ctx; PIN->params = start; PIN->is_latlong = 0; PIN->is_geocent = 0; PIN->is_long_wrap_set = 0; PIN->long_wrap_center = 0.0; strcpy( PIN->axis, "enu" ); PIN->gridlist = nullptr; PIN->gridlist_count = 0; PIN->vgridlist_geoid = nullptr; PIN->vgridlist_geoid_count = 0; /* Set datum parameters. Similarly to +init parameters we want to expand */ /* +datum parameters as late as possible when dealing with pipelines. */ /* otherwise only the first occurrence of +datum will be expanded and that */ if (n_pipelines == 0) { if (pj_datum_set(ctx, start, PIN)) return pj_default_destructor (PIN, proj_errno(PIN)); } err = pj_ellipsoid (PIN); if (err) { /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */ if (PIN->need_ellps) { pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); return pj_default_destructor (PIN, proj_errno(PIN)); } else { if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN)) proj_errno_reset (PIN); PIN->f = 1.0/298.257223563; PIN->a = 6378137.0; PIN->es = PIN->f*(2-PIN->f); } } PIN->a_orig = PIN->a; PIN->es_orig = PIN->es; if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) return pj_default_destructor (PIN, PJD_ERR_INVALID_ECCENTRICITY); /* Now that we have ellipse information check for WGS84 datum */ if( PIN->datum_type == PJD_3PARAM && PIN->datum_params[0] == 0.0 && PIN->datum_params[1] == 0.0 && PIN->datum_params[2] == 0.0 && PIN->a == 6378137.0 && ABS(PIN->es - 0.006694379990) < 0.000000000050 )/*WGS84/GRS80*/ { PIN->datum_type = PJD_WGS84; } /* Set PIN->geoc coordinate system */ PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i); /* Over-ranging flag */ PIN->over = pj_param(ctx, start, "bover").i; /* Vertical datum geoid grids */ PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i; if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */ pj_param(ctx, start, "sgeoidgrids"); /* Longitude center for wrapping */ PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i; if (PIN->is_long_wrap_set) { PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f; /* Don't accept excessive values otherwise we might perform badly */ /* when correcting longitudes around it */ /* The test is written this way to error on long_wrap_center "=" NaN */ if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) ) return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); } /* Axis orientation */ if( (pj_param(ctx, start,"saxis").s) != nullptr ) { const char *axis_legal = "ewnsud"; const char *axis_arg = pj_param(ctx, start,"saxis").s; if( strlen(axis_arg) != 3 ) return pj_default_destructor (PIN, PJD_ERR_AXIS); if( strchr( axis_legal, axis_arg[0] ) == nullptr || strchr( axis_legal, axis_arg[1] ) == nullptr || strchr( axis_legal, axis_arg[2] ) == nullptr) return pj_default_destructor (PIN, PJD_ERR_AXIS); /* TODO: it would be nice to validate we don't have on axis repeated */ strcpy( PIN->axis, axis_arg ); } /* Central meridian */ PIN->lam0=pj_param(ctx, start, "rlon_0").f; /* Central latitude */ PIN->phi0 = pj_param(ctx, start, "rlat_0").f; if( fabs(PIN->phi0) > M_HALFPI ) return pj_default_destructor (PIN, PJD_ERR_LAT_LARGER_THAN_90); /* False easting and northing */ PIN->x0 = pj_param(ctx, start, "dx_0").f; PIN->y0 = pj_param(ctx, start, "dy_0").f; PIN->z0 = pj_param(ctx, start, "dz_0").f; PIN->t0 = pj_param(ctx, start, "dt_0").f; /* General scaling factor */ if (pj_param(ctx, start, "tk_0").i) PIN->k0 = pj_param(ctx, start, "dk_0").f; else if (pj_param(ctx, start, "tk").i) PIN->k0 = pj_param(ctx, start, "dk").f; else PIN->k0 = 1.; if (PIN->k0 <= 0.) return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); /* Set units */ units = proj_list_units(); s = nullptr; if ((name = pj_param(ctx, start, "sunits").s) != nullptr) { for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; if (!s) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); s = units[i].to_meter; } if (s || (s = pj_param(ctx, start, "sto_meter").s)) { char* end_ptr = const_cast(s); PIN->to_meter = pj_strtod(s, &end_ptr); s = end_ptr; if (*s == '/') { /* ratio number */ ++s; double denom = pj_strtod(s, nullptr); if (denom == 0.0) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->to_meter /= denom; } if (PIN->to_meter <= 0.0) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->fr_meter = 1 / PIN->to_meter; } else PIN->to_meter = PIN->fr_meter = 1.; /* Set vertical units */ s = nullptr; if ((name = pj_param(ctx, start, "svunits").s) != nullptr) { for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; if (!s) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); s = units[i].to_meter; } if (s || (s = pj_param(ctx, start, "svto_meter").s)) { char* end_ptr = const_cast(s); PIN->vto_meter = pj_strtod(s, &end_ptr); s = end_ptr; if (*s == '/') { /* ratio number */ ++s; double denom = pj_strtod(s, nullptr); if (denom == 0.0) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->vto_meter /= denom; } if (PIN->vto_meter <= 0.0) return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); PIN->vfr_meter = 1. / PIN->vto_meter; } else { PIN->vto_meter = PIN->to_meter; PIN->vfr_meter = PIN->fr_meter; } /* Prime meridian */ prime_meridians = proj_list_prime_meridians(); s = nullptr; if ((name = pj_param(ctx, start, "spm").s) != nullptr) { const char *value = nullptr; char *next_str = nullptr; for (i = 0; prime_meridians[i].id != nullptr; ++i ) { if( strcmp(name,prime_meridians[i].id) == 0 ) { value = prime_meridians[i].defn; break; } } if( value == nullptr && (dmstor_ctx(ctx,name,&next_str) != 0.0 || *name == '0') && *next_str == '\0' ) value = name; if (!value) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN); PIN->from_greenwich = dmstor_ctx(ctx,value,nullptr); } else PIN->from_greenwich = 0.0; /* Private object for the geodesic functions */ PIN->geod = static_cast(pj_calloc (1, sizeof (struct geod_geodesic))); if (nullptr==PIN->geod) return pj_default_destructor (PIN, ENOMEM); geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es))); /* Projection specific initialization */ err = proj_errno_reset (PIN); PIN = proj(PIN); if (proj_errno (PIN)) { pj_free(PIN); return nullptr; } proj_errno_restore (PIN, err); return PIN; } proj-6.3.1/src/tsfn.cpp0000664000175000017500000000064213601752712011671 00000000000000/* determine small t */ #include #include "proj.h" #include "proj_internal.h" double pj_tsfn(double phi, double sinphi, double e) { double denominator; sinphi *= e; /* avoid zero division, fail gracefully */ denominator = 1.0 + sinphi; if (denominator == 0.0) return HUGE_VAL; return (tan (.5 * (M_HALFPI - phi)) / pow((1. - sinphi) / (denominator), .5 * e)); } proj-6.3.1/src/proj_config.h.in0000664000175000017500000000450013620226607013265 00000000000000/* src/proj_config.h.in. Generated from configure.ac by autoheader. */ /* define if the compiler supports basic C++11 syntax */ #undef HAVE_CXX11 /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H /* Define to 1 if the compiler supports -Wzero-as-null-pointer-constant */ #undef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_JNI_H /* Define to 1 if you have the `m' library (-lm). */ #undef HAVE_LIBM /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have localeconv */ #undef HAVE_LOCALECONV /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define if your pthreads implementation have PTHREAD_MUTEX_RECURSIVE */ #undef HAVE_PTHREAD_MUTEX_RECURSIVE /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Enabled for Java/JNI Support */ #undef JNI_ENABLED /* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION proj-6.3.1/src/errno.cpp0000664000175000017500000000062413601752712012044 00000000000000/* For full ANSI compliance of global variable */ #include "proj.h" #include "proj_internal.h" int pj_errno = 0; /************************************************************************/ /* pj_get_errno_ref() */ /************************************************************************/ int *pj_get_errno_ref() { return &pj_errno; } /* end */ proj-6.3.1/src/strerrno.cpp0000664000175000017500000001423113601752712012574 00000000000000/* list of projection system pj_errno values */ #include #include #include #include "proj.h" #include "proj_config.h" #include "proj_internal.h" static const char * const pj_err_list[] = { "no arguments in initialization list", /* -1 */ "no options found in 'init' file", /* -2 */ "no colon in init= string", /* -3 */ "projection not named", /* -4 */ "unknown projection id", /* -5 */ "effective eccentricity < 0 or >= 1.", /* -6 */ "unknown unit conversion id", /* -7 */ "invalid boolean param argument", /* -8 */ "unknown elliptical parameter name", /* -9 */ "reciprocal flattening (1/f) = 0", /* -10 */ "|radius reference latitude| > 90", /* -11 */ "squared eccentricity < 0", /* -12 */ "major axis or radius = 0 or not given", /* -13 */ "latitude or longitude exceeded limits", /* -14 */ "invalid x or y", /* -15 */ "improperly formed DMS value", /* -16 */ "non-convergent inverse meridional dist", /* -17 */ "non-convergent inverse phi2", /* -18 */ "acos/asin: |arg| >1.+1e-14", /* -19 */ "tolerance condition error", /* -20 */ "conic lat_1 = -lat_2", /* -21 */ "lat_0, lat_1 or lat_2 >= 90", /* -22 */ "lat_1 = 0", /* -23 */ "lat_ts >= 90", /* -24 */ "no distance between control points", /* -25 */ "projection not selected to be rotated", /* -26 */ "W <= 0 or M <= 0", /* -27 */ "lsat not in 1-5 range", /* -28 */ "path not in range", /* -29 */ "h <= 0 or h > 1e10 * a", /* -30 */ "k <= 0", /* -31 */ "lat_1=lat_2 or lat_1=0 or lat_2=90", /* -32 */ "lat_0 = 0 or 90 or alpha = 90", /* -33 */ "elliptical usage required", /* -34 */ "invalid UTM zone number", /* -35 */ "", /* no longer used */ /* -36 */ "failed to find projection to be rotated", /* -37 */ "failed to load datum shift file", /* -38 */ "both n & m must be spec'd and > 0", /* -39 */ "n <= 0, n > 1 or not specified", /* -40 */ "lat_1 or lat_2 not specified", /* -41 */ "|lat_1| == |lat_2|", /* -42 */ "lat_0 is pi/2 from mean lat", /* -43 */ "unparseable coordinate system definition", /* -44 */ "geocentric transformation missing z or ellps", /* -45 */ "unknown prime meridian conversion id", /* -46 */ "illegal axis orientation combination", /* -47 */ "point not within available datum shift grids", /* -48 */ "invalid sweep axis, choose x or y", /* -49 */ "malformed pipeline", /* -50 */ "unit conversion factor must be > 0", /* -51 */ "invalid scale", /* -52 */ "non-convergent computation", /* -53 */ "missing required arguments", /* -54 */ "lat_0 = 0", /* -55 */ "ellipsoidal usage unsupported", /* -56 */ "only one +init allowed for non-pipeline operations", /* -57 */ "argument not numerical or out of range", /* -58 */ "inconsistent unit type between input and output", /* -59 */ "arguments are mutually exclusive", /* -60 */ "generic error of unknown origin", /* -61 */ /* When adding error messages, remember to update ID defines in projects.h, and transient_error array in pj_transform */ }; char *pj_strerrno(int err) { const int max_error = 9999; static char note[50]; size_t adjusted_err; if (0==err) return nullptr; /* System error codes are positive */ if (err > 0) { #ifdef HAVE_STRERROR return strerror(err); #else /* Defend string boundary against exorbitantly large err values */ /* which may occur on platforms with 64-bit ints */ sprintf(note, "no system list, errno: %d\n", (err < max_error) ? err: max_error); return note; #endif } /* PROJ.4 error codes are negative: -1 to -9999 */ adjusted_err = err < -max_error ? max_error : -err - 1; if (adjusted_err < (sizeof(pj_err_list) / sizeof(char *))) return (char *)pj_err_list[adjusted_err]; sprintf(note, "invalid projection system error (%d)", (err > -max_error) ? err: -max_error); return note; } const char* proj_errno_string(int err) { return pj_strerrno(err); } proj-6.3.1/src/inv.cpp0000664000175000017500000001667713601752712011532 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Inverse operation invocation * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 * Based on material from Gerald Evenden (original pj_inv) * and Piyush Agram (original pj_inv3d) * ****************************************************************************** * Copyright (c) 2000, Frank Warmerdam * Copyright (c) 2018, Thomas Knudsen / SDFE * * 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 #include #include "proj_internal.h" #include #define INPUT_UNITS P->right #define OUTPUT_UNITS P->left static PJ_COORD inv_prepare (PJ *P, PJ_COORD coo) { if (coo.v[0] == HUGE_VAL || coo.v[1] == HUGE_VAL || coo.v[2] == HUGE_VAL) { proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); return proj_coord_error (); } /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; if (P->axisswap) coo = proj_trans (P->axisswap, PJ_INV, coo); /* Handle remaining possible input types */ switch (INPUT_UNITS) { case PJ_IO_UNITS_WHATEVER: return coo; /* de-scale and de-offset */ case PJ_IO_UNITS_CARTESIAN: coo.xyz.x *= P->to_meter; coo.xyz.y *= P->to_meter; coo.xyz.z *= P->to_meter; if (P->is_geocent) { coo = proj_trans (P->cart, PJ_INV, coo); } return coo; case PJ_IO_UNITS_PROJECTED: case PJ_IO_UNITS_CLASSIC: coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0; if (INPUT_UNITS==PJ_IO_UNITS_PROJECTED) return coo; /* Classic proj.4 functions expect plane coordinates in units of the semimajor axis */ /* Multiplying by ra, rather than dividing by a because the CalCOFI projection */ /* stomps on a and hence (apparently) depends on this to roundtrip correctly */ /* (CalCOFI avoids further scaling by stomping - but a better solution is possible) */ coo.xyz.x *= P->ra; coo.xyz.y *= P->ra; return coo; case PJ_IO_UNITS_RADIANS: coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0; break; } /* Should not happen, so we could return pj_coord_err here */ return coo; } static PJ_COORD inv_finalize (PJ *P, PJ_COORD coo) { if (coo.xyz.x == HUGE_VAL) { proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); return proj_coord_error (); } if (OUTPUT_UNITS==PJ_IO_UNITS_RADIANS) { /* Distance from central meridian, taking system zero meridian into account */ coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; /* adjust longitude to central meridian */ if (0==P->over) coo.lpz.lam = adjlon(coo.lpz.lam); if (P->vgridshift) coo = proj_trans (P->vgridshift, PJ_INV, coo); /* Go geometric from orthometric */ if (coo.lp.lam==HUGE_VAL) return coo; if (P->hgridshift) coo = proj_trans (P->hgridshift, PJ_FWD, coo); else if (P->helmert || (P->cart_wgs84 != nullptr && P->cart != nullptr)) { coo = proj_trans (P->cart, PJ_FWD, coo); /* Go cartesian in local frame */ if( P->helmert ) coo = proj_trans (P->helmert, PJ_FWD, coo); /* Step into WGS84 */ coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ } if (coo.lp.lam==HUGE_VAL) return coo; /* If input latitude was geocentrical, convert back to geocentrical */ if (P->geoc) coo = pj_geocentric_latitude (P, PJ_FWD, coo); } return coo; } static PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { if (proj_errno(P)) return proj_coord_error(); proj_errno_restore(P, last_errno); return coord; } PJ_LP pj_inv(PJ_XY xy, PJ *P) { int last_errno; PJ_COORD coo = {{0,0,0,0}}; coo.xy = xy; last_errno = proj_errno_reset(P); if (!P->skip_inv_prepare) coo = inv_prepare (P, coo); if (HUGE_VAL==coo.v[0]) return proj_coord_error ().lp; /* Do the transformation, using the lowest dimensional transformer available */ if (P->inv) coo.lp = P->inv(coo.xy, P); else if (P->inv3d) coo.lpz = P->inv3d (coo.xyz, P); else if (P->inv4d) coo = P->inv4d (coo, P); else { proj_errno_set (P, EINVAL); return proj_coord_error ().lp; } if (HUGE_VAL==coo.v[0]) return proj_coord_error ().lp; if (!P->skip_inv_finalize) coo = inv_finalize (P, coo); return error_or_coord(P, coo, last_errno).lp; } PJ_LPZ pj_inv3d (PJ_XYZ xyz, PJ *P) { int last_errno; PJ_COORD coo = {{0,0,0,0}}; coo.xyz = xyz; last_errno = proj_errno_reset(P); if (!P->skip_inv_prepare) coo = inv_prepare (P, coo); if (HUGE_VAL==coo.v[0]) return proj_coord_error ().lpz; /* Do the transformation, using the lowest dimensional transformer feasible */ if (P->inv3d) coo.lpz = P->inv3d (coo.xyz, P); else if (P->inv4d) coo = P->inv4d (coo, P); else if (P->inv) coo.lp = P->inv (coo.xy, P); else { proj_errno_set (P, EINVAL); return proj_coord_error ().lpz; } if (HUGE_VAL==coo.v[0]) return proj_coord_error ().lpz; if (!P->skip_inv_finalize) coo = inv_finalize (P, coo); return error_or_coord(P, coo, last_errno).lpz; } PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { int last_errno = proj_errno_reset(P); if (!P->skip_inv_prepare) coo = inv_prepare (P, coo); if (HUGE_VAL==coo.v[0]) return proj_coord_error (); /* Call the highest dimensional converter available */ if (P->inv4d) coo = P->inv4d (coo, P); else if (P->inv3d) coo.lpz = P->inv3d (coo.xyz, P); else if (P->inv) coo.lp = P->inv (coo.xy, P); else { proj_errno_set (P, EINVAL); return proj_coord_error (); } if (HUGE_VAL==coo.v[0]) return proj_coord_error (); if (!P->skip_inv_finalize) coo = inv_finalize (P, coo); return error_or_coord(P, coo, last_errno); } proj-6.3.1/src/apps/0000775000175000017500000000000013620226610011226 500000000000000proj-6.3.1/src/apps/cs2cs.cpp0000664000175000017500000005625313601752712012710 00000000000000/****************************************************************************** * Project: PROJ.4 * Purpose: Mainline program sort of like ``proj'' for converting between * two coordinate systems. * Author: Frank Warmerdam, warmerda@home.com * ****************************************************************************** * Copyright (c) 2000, 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. *****************************************************************************/ #define FROM_PROJ_CPP #include #include #include #include #include #include #include #include #include // PROJ include order is sensitive // clang-format off #include "proj.h" #include "proj_experimental.h" #include "proj_internal.h" #include "emess.h" #include "utils.h" // clang-format on #define MAX_LINE 1000 static PJ *transformation = nullptr; static bool srcIsGeog = false; static double srcToRadians = 0.0; static bool destIsGeog = false; static double destToRadians = 0.0; static bool destIsLatLong = false; static int reversein = 0, /* != 0 reverse input arguments */ reverseout = 0, /* != 0 reverse output arguments */ echoin = 0, /* echo input data to output line */ tag = '#'; /* beginning of line tag character */ static const char *oform = nullptr; /* output format for x-y or decimal degrees */ static char oform_buffer[16]; /* buffer for oform when using -d */ static const char *oterr = "*\t*"; /* output line for unprojectable input */ static const char *usage = "%s\nusage: %s [-dDeEfIlrstvwW [args]] [+opt[=arg] ...]\n" " [+to +opt[=arg] ...] [file ...]\n"; static double (*informat)(const char *, char **); /* input data deformatter function */ /************************************************************************/ /* process() */ /* */ /* File processing function. */ /************************************************************************/ static void process(FILE *fid) { char line[MAX_LINE + 3], *s, pline[40]; PJ_UV data; for (;;) { double z; ++emess_dat.File_line; if (!(s = fgets(line, MAX_LINE, fid))) break; if (!strchr(s, '\n')) { /* overlong line */ int c; (void)strcat(s, "\n"); /* gobble up to newline */ while ((c = fgetc(fid)) != EOF && c != '\n') ; } if (*s == tag) { fputs(line, stdout); continue; } if (reversein) { data.v = (*informat)(s, &s); data.u = (*informat)(s, &s); } else { data.u = (*informat)(s, &s); data.v = (*informat)(s, &s); } z = strtod(s, &s); /* To avoid breaking existing tests, we read what is a possible t */ /* component of the input and rewind the s-pointer so that the final */ /* output has consistent behaviour, with or without t values. */ /* This is a bit of a hack, in most cases 4D coordinates will be */ /* written to STDOUT (except when using -E) but the output format */ /* speficied with -f is not respected for the t component, rather it */ /* is forward verbatim from the input. */ char *before_time = s; double t = strtod(s, &s); if( s == before_time ) t = HUGE_VAL; s = before_time; if (data.v == HUGE_VAL) data.u = HUGE_VAL; if (!*s && (s > line)) --s; /* assumed we gobbled \n */ if (echoin) { char temp; temp = *s; *s = '\0'; (void)fputs(line, stdout); *s = temp; putchar('\t'); } if (data.u != HUGE_VAL) { if (srcIsGeog) { /* dmstor gives values to radians. Convert now to the SRS unit */ data.u /= srcToRadians; data.v /= srcToRadians; } PJ_COORD coord; coord.xyzt.x = data.u; coord.xyzt.y = data.v; coord.xyzt.z = z; coord.xyzt.t = t; coord = proj_trans(transformation, PJ_FWD, coord); data.u = coord.xyz.x; data.v = coord.xyz.y; z = coord.xyz.z; } if (data.u == HUGE_VAL) /* error output */ fputs(oterr, stdout); else if (destIsGeog && !oform) { /*ascii DMS output */ // rtodms() expect radians: convert from the output SRS unit data.u *= destToRadians; data.v *= destToRadians; if (destIsLatLong) { if (reverseout) { fputs(rtodms(pline, data.v, 'E', 'W'), stdout); putchar('\t'); fputs(rtodms(pline, data.u, 'N', 'S'), stdout); } else { fputs(rtodms(pline, data.u, 'N', 'S'), stdout); putchar('\t'); fputs(rtodms(pline, data.v, 'E', 'W'), stdout); } } else if (reverseout) { fputs(rtodms(pline, data.v, 'N', 'S'), stdout); putchar('\t'); fputs(rtodms(pline, data.u, 'E', 'W'), stdout); } else { fputs(rtodms(pline, data.u, 'E', 'W'), stdout); putchar('\t'); fputs(rtodms(pline, data.v, 'N', 'S'), stdout); } } else { /* x-y or decimal degree ascii output */ if (destIsGeog) { data.v *= destToRadians * RAD_TO_DEG; data.u *= destToRadians * RAD_TO_DEG; } if (reverseout) { printf(oform, data.v); putchar('\t'); printf(oform, data.u); } else { printf(oform, data.u); putchar('\t'); printf(oform, data.v); } } putchar(' '); if (oform != nullptr) printf(oform, z); else printf("%.3f", z); if (s) printf("%s", s); else printf("\n"); } } /************************************************************************/ /* instantiate_crs() */ /************************************************************************/ static PJ *instantiate_crs(const std::string &definition, bool &isGeog, double &toRadians, bool &isLatFirst) { PJ *crs = proj_create(nullptr, pj_add_type_crs_if_needed(definition).c_str()); if (!crs) { return nullptr; } isGeog = false; toRadians = 0.0; isLatFirst = false; auto type = proj_get_type(crs); if (type == PJ_TYPE_BOUND_CRS) { auto base = proj_get_source_crs(nullptr, crs); proj_destroy(crs); crs = base; type = proj_get_type(crs); } if (type == PJ_TYPE_GEOGRAPHIC_2D_CRS || type == PJ_TYPE_GEOGRAPHIC_3D_CRS) { auto cs = proj_crs_get_coordinate_system(nullptr, crs); assert(cs); isGeog = true; const char *axisName = ""; proj_cs_get_axis_info(nullptr, cs, 0, &axisName, // name, nullptr, // abbrev nullptr, // direction &toRadians, nullptr, // unit name nullptr, // unit authority nullptr // unit code ); isLatFirst = NS_PROJ::internal::ci_find(std::string(axisName), "latitude") != std::string::npos; proj_destroy(cs); } return crs; } /************************************************************************/ /* get_geog_crs_proj_string_from_proj_crs() */ /************************************************************************/ static std::string get_geog_crs_proj_string_from_proj_crs(PJ *src, double &toRadians, bool &isLatFirst) { auto srcType = proj_get_type(src); if (srcType != PJ_TYPE_PROJECTED_CRS) { return std::string(); } auto base = proj_get_source_crs(nullptr, src); assert(base); auto baseType = proj_get_type(base); if (baseType != PJ_TYPE_GEOGRAPHIC_2D_CRS && baseType != PJ_TYPE_GEOGRAPHIC_3D_CRS) { proj_destroy(base); return std::string(); } auto cs = proj_crs_get_coordinate_system(nullptr, base); assert(cs); const char *axisName = ""; proj_cs_get_axis_info(nullptr, cs, 0, &axisName, // name, nullptr, // abbrev nullptr, // direction &toRadians, nullptr, // unit name nullptr, // unit authority nullptr // unit code ); isLatFirst = NS_PROJ::internal::ci_find(std::string(axisName), "latitude") != std::string::npos; proj_destroy(cs); auto retCStr = proj_as_proj_string(nullptr, base, PJ_PROJ_5, nullptr); std::string ret(retCStr ? retCStr : ""); proj_destroy(base); return ret; } /************************************************************************/ /* main() */ /************************************************************************/ int main(int argc, char **argv) { char *arg; char **eargv = argv; std::string fromStr; std::string toStr; FILE *fid; int eargc = 0, mon = 0; int have_to_flag = 0, inverse = 0; int use_env_locale = 0; /* This is just to check that pj_init() is locale-safe */ /* Used by nad/testvarious */ if (getenv("PROJ_USE_ENV_LOCALE") != nullptr) use_env_locale = 1; /* Enable compatibility mode for init=epsg:XXXX by default */ if (getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr) { proj_context_use_proj4_init_rules(nullptr, true); } if ((emess_dat.Prog_name = strrchr(*argv, DIR_CHAR)) != nullptr) ++emess_dat.Prog_name; else emess_dat.Prog_name = *argv; inverse = !strncmp(emess_dat.Prog_name, "inv", 3); if (argc <= 1) { (void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name); exit(0); } // First pass to check if we have "cs2cs [-bla]* " syntax int countNonOptionArg = 0; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (argv[i][1] == 'f' || argv[i][1] == 'e' || argv[i][1] == 'd' || argv[i][1] == 'D' ) { i++; } } else { if (strcmp(argv[i], "+to") == 0) { countNonOptionArg = -1; break; } countNonOptionArg++; } } const bool isSrcDestSyntax = (countNonOptionArg == 2); /* process run line arguments */ while (--argc > 0) { /* collect run line arguments */ if (**++argv == '-') { for (arg = *argv;;) { switch (*++arg) { case '\0': /* position of "stdin" */ if (arg[-1] == '-') eargv[eargc++] = const_cast("-"); break; case 'v': /* monitor dump of initialization */ mon = 1; continue; case 'I': /* alt. method to spec inverse */ inverse = 1; continue; case 'E': /* echo ascii input to ascii output */ echoin = 1; continue; case 't': /* set col. one char */ if (arg[1]) tag = *++arg; else emess(1, "missing -t col. 1 tag"); continue; case 'l': /* list projections, ellipses or units */ if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') { /* list projections */ const struct PJ_LIST *lp; int do_long = arg[1] == 'P', c; const char *str; for (lp = proj_list_operations(); lp->id; ++lp) { (void)printf("%s : ", lp->id); if (do_long) /* possibly multiline description */ (void)puts(*lp->descr); else { /* first line, only */ str = *lp->descr; while ((c = *str++) && c != '\n') putchar(c); putchar('\n'); } } } else if (arg[1] == '=') { /* list projection 'descr' */ const struct PJ_LIST *lp; arg += 2; for (lp = proj_list_operations(); lp->id; ++lp) if (!strcmp(lp->id, arg)) { (void)printf("%9s : %s\n", lp->id, *lp->descr); break; } } else if (arg[1] == 'e') { /* list ellipses */ const struct PJ_ELLPS *le; for (le = proj_list_ellps(); le->id; ++le) (void)printf("%9s %-16s %-16s %s\n", le->id, le->major, le->ell, le->name); } else if (arg[1] == 'u') { /* list units */ const struct PJ_UNITS *lu; for (lu = proj_list_units(); lu->id; ++lu) (void)printf("%12s %-20s %s\n", lu->id, lu->to_meter, lu->name); } else if (arg[1] == 'd') { /* list datums */ const struct PJ_DATUMS *ld; printf("__datum_id__ __ellipse___ " "__definition/" "comments______________________________\n"); for (ld = pj_get_datums_ref(); ld->id; ++ld) { printf("%12s %-12s %-30s\n", ld->id, ld->ellipse_id, ld->defn); if (ld->comments != nullptr && strlen(ld->comments) > 0) printf("%25s %s\n", " ", ld->comments); } } else if (arg[1] == 'm') { /* list prime meridians */ const struct PJ_PRIME_MERIDIANS *lpm; for (lpm = proj_list_prime_meridians(); lpm->id; ++lpm) (void)printf("%12s %-30s\n", lpm->id, lpm->defn); } else emess(1, "invalid list option: l%c", arg[1]); exit(0); /* cppcheck-suppress duplicateBreak */ continue; /* artificial */ case 'e': /* error line alternative */ if (--argc <= 0) noargument: emess(1, "missing argument for -%c", *arg); oterr = *++argv; continue; case 'W': /* specify seconds precision */ case 'w': /* -W for constant field width */ { char c = arg[1]; if (c != 0 && isdigit(c)) { set_rtodms(c - '0', *arg == 'W'); ++arg; } else emess(1, "-W argument missing or non-digit"); continue; } case 'f': /* alternate output format degrees or xy */ if (--argc <= 0) goto noargument; oform = *++argv; continue; case 'r': /* reverse input */ reversein = 1; continue; case 's': /* reverse output */ reverseout = 1; continue; case 'D': /* set debug level */ if (--argc <= 0) goto noargument; pj_ctx_set_debug(pj_get_default_ctx(), atoi(*++argv)); continue; case 'd': if (--argc <= 0) goto noargument; sprintf(oform_buffer, "%%.%df", atoi(*++argv)); oform = oform_buffer; break; default: emess(1, "invalid option: -%c", *arg); break; } break; } } else if (isSrcDestSyntax) { if (fromStr.empty()) fromStr = *argv; else toStr = *argv; } else if (strcmp(*argv, "+to") == 0) { have_to_flag = 1; } else if (**argv == '+') { /* + argument */ if (have_to_flag) { if (!toStr.empty()) toStr += ' '; toStr += *argv; } else { if (!fromStr.empty()) fromStr += ' '; fromStr += *argv; } } else if (!have_to_flag) { fromStr = *argv; } else if (toStr.empty()) { toStr = *argv; } else /* assumed to be input file name(s) */ eargv[eargc++] = *argv; } if (eargc == 0) /* if no specific files force sysin */ eargv[eargc++] = const_cast("-"); if( oform ) { if( !validate_form_string_for_numbers(oform) ) { emess(3, "invalid format string"); exit(0); } } /* * If the user has requested inverse, then just reverse the * coordinate systems. */ if (inverse) { std::swap(fromStr, toStr); } if (use_env_locale) { /* Set locale from environment */ setlocale(LC_ALL, ""); } if (fromStr.empty() && toStr.empty()) { emess(3, "missing source and target coordinate systems"); } proj_context_use_proj4_init_rules(nullptr, proj_context_get_use_proj4_init_rules(nullptr, TRUE)); PJ *src = nullptr; if (!fromStr.empty()) { bool ignored; src = instantiate_crs(fromStr, srcIsGeog, srcToRadians, ignored); if (!src) { emess(3, "cannot instantiate source coordinate system"); } } PJ *dst = nullptr; if (!toStr.empty()) { dst = instantiate_crs(toStr, destIsGeog, destToRadians, destIsLatLong); if (!dst) { emess(3, "cannot instantiate target coordinate system"); } } if (toStr.empty()) { assert(src); toStr = get_geog_crs_proj_string_from_proj_crs(src, destToRadians, destIsLatLong); if (toStr.empty()) { emess(3, "missing target CRS and source CRS is not a projected CRS"); } destIsGeog = true; } else if (fromStr.empty()) { assert(dst); bool ignored; fromStr = get_geog_crs_proj_string_from_proj_crs(dst, srcToRadians, ignored); if (fromStr.empty()) { emess(3, "missing source CRS and target CRS is not a projected CRS"); } srcIsGeog = true; } proj_destroy(src); proj_destroy(dst); src = proj_create(nullptr, pj_add_type_crs_if_needed(fromStr).c_str()); dst = proj_create(nullptr, pj_add_type_crs_if_needed(toStr).c_str()); if( proj_get_type(src) == PJ_TYPE_COMPOUND_CRS || proj_get_type(dst) == PJ_TYPE_COMPOUND_CRS ) { auto src3D = proj_crs_promote_to_3D(nullptr, nullptr, src); if( src3D ) { proj_destroy(src); src = src3D; } auto dst3D = proj_crs_promote_to_3D(nullptr, nullptr, dst); if( dst3D ) { proj_destroy(dst); dst = dst3D; } } transformation = proj_create_crs_to_crs_from_pj(nullptr, src, dst, nullptr, nullptr); proj_destroy(src); proj_destroy(dst); if (!transformation) { emess(3, "cannot initialize transformation\ncause: %s", pj_strerrno(pj_errno)); } if (use_env_locale) { /* Restore C locale to avoid issues in parsing/outputting numbers*/ setlocale(LC_ALL, "C"); } if (mon) { printf("%c ---- From Coordinate System ----\n", tag); printf("%s\n", fromStr.c_str()); printf("%c ---- To Coordinate System ----\n", tag); printf("%s\n", toStr.c_str()); } /* set input formatting control */ if (!srcIsGeog) informat = strtod; else { informat = dmstor; } if (!destIsGeog && !oform) oform = "%.2f"; /* process input file list */ for (; eargc--; ++eargv) { if (**eargv == '-') { fid = stdin; emess_dat.File_name = const_cast(""); } else { if ((fid = fopen(*eargv, "rt")) == nullptr) { emess(-2, *eargv, "input file"); continue; } emess_dat.File_name = *eargv; } emess_dat.File_line = 0; process(fid); fclose(fid); emess_dat.File_name = nullptr; } proj_destroy(transformation); proj_cleanup(); exit(0); /* normal completion */ } proj-6.3.1/src/apps/cct.cpp0000664000175000017500000004031313601752712012432 00000000000000/*********************************************************************** The cct 4D Transformation program ************************************************************************ cct is a 4D equivalent to the "proj" projection program. cct is an acronym meaning "Coordinate Conversion and Transformation". The acronym refers to definitions given in the OGC 08-015r2/ISO-19111 standard "Geographical Information -- Spatial Referencing by Coordinates", which defines two different classes of coordinate operations: *Coordinate Conversions*, which are coordinate operations where input and output datum are identical (e.g. conversion from geographical to cartesian coordinates) and *Coordinate Transformations*, which are coordinate operations where input and output datums differ (e.g. change of reference frame). cct, however, also refers to Carl Christian Tscherning (1942--2014), professor of Geodesy at the University of Copenhagen, mentor and advisor for a generation of Danish geodesists, colleague and collaborator for two generations of global geodesists, Secretary General for the International Association of Geodesy, IAG (1995--2007), fellow of the American Geophysical Union (1991), recipient of the IAG Levallois Medal (2007), the European Geosciences Union Vening Meinesz Medal (2008), and of numerous other honours. cct, or Christian, as he was known to most of us, was recognized for his good mood, his sharp wit, his tireless work, and his great commitment to the development of geodesy - both through his scientific contributions, comprising more than 250 publications, and by his mentoring and teaching of the next generations of geodesists. As Christian was an avid Fortran programmer, and a keen Unix connoisseur, he would have enjoyed to know that his initials would be used to name a modest Unix style transformation filter, hinting at the tireless aspect of his personality, which was certainly one of the reasons he accomplished so much, and meant so much to so many people. Hence, in honour of cct (the geodesist) this is cct (the program). ************************************************************************ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 ************************************************************************ * Copyright (c) 2016, 2017 Thomas Knudsen * Copyright (c) 2017, SDFE * * 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 #include #include #include #include #include #include "proj.h" #include "proj_internal.h" #include "proj_strtod.h" #include "optargpm.h" static void logger(void *data, int level, const char *msg); static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...); /* Prototypes from functions in this file */ char *column (char *buf, int n); PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time); static const char usage[] = { "--------------------------------------------------------------------------------\n" "Usage: %s [-options]... [+operator_specs]... infile...\n" "--------------------------------------------------------------------------------\n" "Options:\n" "--------------------------------------------------------------------------------\n" " -c x,y,z,t Specify input columns for (up to) 4 input parameters.\n" " Defaults to 1,2,3,4\n" " -d n Specify number of decimals in output.\n" " -I Do the inverse transformation\n" " -o /path/to/file Specify output file name\n" " -t value Provide a fixed t value for all input data (e.g. -t 0)\n" " -z value Provide a fixed z value for all input data (e.g. -z 0)\n" " -s n Skip n first lines of a infile\n" " -v Verbose: Provide non-essential informational output.\n" " Repeat -v for more verbosity (e.g. -vv)\n" "--------------------------------------------------------------------------------\n" "Long Options:\n" "--------------------------------------------------------------------------------\n" " --output Alias for -o\n" " --columns Alias for -c\n" " --decimals Alias for -d\n" " --height Alias for -z\n" " --time Alias for -t\n" " --verbose Alias for -v\n" " --inverse Alias for -I\n" " --skip-lines Alias for -s\n" " --help Alias for -h\n" " --version Print version number\n" "--------------------------------------------------------------------------------\n" "Operator Specs:\n" "--------------------------------------------------------------------------------\n" "The operator specs describe the action to be performed by cct, e.g:\n" "\n" " +proj=utm +ellps=GRS80 +zone=32\n" "\n" "instructs cct to convert input data to Universal Transverse Mercator, zone 32\n" "coordinates, based on the GRS80 ellipsoid.\n" "\n" "Hence, the command\n" "\n" " echo 12 55 | cct -z0 -t0 +proj=utm +zone=32 +ellps=GRS80\n" "\n" "Should give results comparable to the classic proj command\n" "\n" " echo 12 55 | proj +proj=utm +zone=32 +ellps=GRS80\n" "--------------------------------------------------------------------------------\n" "Examples:\n" "--------------------------------------------------------------------------------\n" "1. convert geographical input to UTM zone 32 on the GRS80 ellipsoid:\n" " cct +proj=utm +ellps=GRS80 +zone=32\n" "2. roundtrip accuracy check for the case above:\n" " cct +proj=pipeline +proj=utm +ellps=GRS80 +zone=32 +step +step +inv\n" "3. as (1) but specify input columns for longitude, latitude, height and time:\n" " cct -c 5,2,1,4 +proj=utm +ellps=GRS80 +zone=32\n" "4. as (1) but specify fixed height and time, hence needing only 2 cols in input:\n" " cct -t 0 -z 0 +proj=utm +ellps=GRS80 +zone=32\n" "--------------------------------------------------------------------------------\n" }; static void logger(void *data, int level, const char *msg) { FILE *stream; int log_tell = proj_log_level(PJ_DEFAULT_CTX, PJ_LOG_TELL); stream = (FILE *) data; /* if we use PJ_LOG_NONE we always want to print stuff to stream */ if (level == PJ_LOG_NONE) { fprintf(stream, "%s\n", msg); return; } /* otherwise only print if log level set by user is high enough or error */ if (level <= log_tell || level == PJ_LOG_ERROR) fprintf(stderr, "%s\n", msg); } FILE *fout; static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...) { va_list args; char *msg_buf; va_start( args, fmt ); msg_buf = (char *) malloc(100000); if( msg_buf == nullptr ) { va_end( args ); return; } vsprintf( msg_buf, fmt, args ); logger((void *) fout, log_level, msg_buf); va_end( args ); free( msg_buf ); } int main(int argc, char **argv) { PJ *P; PJ_COORD point; PJ_PROJ_INFO info; OPTARGS *o; char blank_comment[] = ""; char whitespace[] = " "; char *comment; char *comment_delimiter; char *buf; int i, nfields = 4, skip_lines = 0, verbose; double fixed_z = HUGE_VAL, fixed_time = HUGE_VAL; int decimals_angles = 10; int decimals_distances = 4; int columns_xyzt[] = {1, 2, 3, 4}; const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", nullptr}; const char *longkeys[] = { "o=output", "c=columns", "d=decimals", "z=height", "t=time", "s=skip-lines", nullptr}; fout = stdout; /* coverity[tainted_data] */ o = opt_parse (argc, argv, "hvI", "cdozts", longflags, longkeys); if (nullptr==o) return 0; if (opt_given (o, "h") || argc==1) { printf (usage, o->progname); return 0; } PJ_DIRECTION direction = opt_given (o, "I")? PJ_INV: PJ_FWD; verbose = MIN(opt_given (o, "v"), 3); /* log level can't be larger than 3 */ if( verbose > 0 ) { proj_log_level (PJ_DEFAULT_CTX, static_cast(verbose)); } proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); if (opt_given (o, "version")) { print (PJ_LOG_NONE, "%s: %s", o->progname, pj_get_release ()); return 0; } if (opt_given (o, "o")) fout = fopen (opt_arg (o, "output"), "wt"); if (nullptr==fout) { print (PJ_LOG_ERROR, "%s: Cannot open '%s' for output", o->progname, opt_arg (o, "output")); free (o); return 1; } print (PJ_LOG_TRACE, "%s: Running in very verbose mode", o->progname); if (opt_given (o, "z")) { fixed_z = proj_atof (opt_arg (o, "z")); nfields--; } if (opt_given (o, "t")) { fixed_time = proj_atof (opt_arg (o, "t")); nfields--; } if (opt_given (o, "d")) { int dec = atoi (opt_arg (o, "d")); decimals_angles = dec; decimals_distances = dec; } if (opt_given (o, "s")) { skip_lines = atoi (opt_arg(o, "s")); } if (opt_given (o, "c")) { int ncols; /* reset column numbers to ease comment output later on */ for (i=0; i<4; i++) columns_xyzt[i] = 0; /* cppcheck-suppress invalidscanf */ ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+2, columns_xyzt+3); if (ncols != nfields) { print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'", o->progname, opt_arg (o, "c")); free (o); if (stdout != fout) fclose (fout); return 1; } } /* Setup transformation */ P = proj_create_argv (nullptr, o->pargc, o->pargv); if ((nullptr==P) || (0==o->pargc)) { print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help", o->progname, pj_strerrno (proj_errno(P)), o->progname); free (o); if (stdout != fout) fclose (fout); return 1; } info = proj_pj_info (P); print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d", info.definition, argc, o->pargc); if (direction== PJ_INV) { /* fail if an inverse operation is not available */ if (!info.has_inverse) { print (PJ_LOG_ERROR, "Inverse operation not available"); if (stdout != fout) fclose (fout); return 1; } /* We have no API call for inverting an operation, so we brute force it. */ P->inverted = !(P->inverted); } direction = PJ_FWD; /* Allocate input buffer */ buf = static_cast(calloc (1, 10000)); if (nullptr==buf) { print (PJ_LOG_ERROR, "%s: Out of memory", o->progname); pj_free (P); free (o); if (stdout != fout) fclose (fout); return 1; } /* Loop over all records of all input files */ while (opt_input_loop (o, optargs_file_format_text)) { int err; void *ret = fgets (buf, 10000, o->input); char *c = column (buf, 1); opt_eof_handler (o); if (nullptr==ret) { print (PJ_LOG_ERROR, "Read error in record %d", (int) o->record_index); continue; } point = parse_input_line (buf, columns_xyzt, fixed_z, fixed_time); if (skip_lines > 0) { skip_lines--; continue; } /* if it's a comment or blank line, we reflect it */ if (c && ((*c=='\0') || (*c=='#'))) { fprintf (fout, "%s", buf); continue; } if (HUGE_VAL==point.xyzt.x) { /* otherwise, it must be a syntax error */ print (PJ_LOG_NONE, "# Record %d UNREADABLE: %s", (int) o->record_index, buf); print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d", o->progname, opt_filename (o), opt_record (o)); continue; } if (proj_angular_input (P, direction)) { point.lpzt.lam = proj_torad (point.lpzt.lam); point.lpzt.phi = proj_torad (point.lpzt.phi); } err = proj_errno_reset (P); /* coverity[returned_value] */ point = proj_trans (P, direction, point); if (HUGE_VAL==point.xyzt.x) { /* transformation error */ print (PJ_LOG_NONE, "# Record %d TRANSFORMATION ERROR: %s (%s)", (int) o->record_index, buf, pj_strerrno (proj_errno(P))); proj_errno_restore (P, err); continue; } proj_errno_restore (P, err); /* handle comment string */ comment = column(buf, nfields+1); if (opt_given(o, "c")) { /* what number is the last coordinate column in the input data? */ int colmax = 0; for (i=0; i<4; i++) colmax = MAX(colmax, columns_xyzt[i]); comment = column(buf, colmax+1); } comment_delimiter = (comment && *comment) ? whitespace : blank_comment; /* Time to print the result */ if (proj_angular_output (P, direction)) { point.lpzt.lam = proj_todeg (point.lpzt.lam); point.lpzt.phi = proj_todeg (point.lpzt.phi); print (PJ_LOG_NONE, "%14.*f %14.*f %12.*f %12.4f%s%s", decimals_angles, point.xyzt.x, decimals_angles, point.xyzt.y, decimals_distances, point.xyzt.z, point.xyzt.t, comment_delimiter, comment ); } else print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s", decimals_distances, point.xyzt.x, decimals_distances, point.xyzt.y, decimals_distances, point.xyzt.z, point.xyzt.t, comment_delimiter, comment ); } proj_destroy(P); if (stdout != fout) fclose (fout); free (o); free (buf); return 0; } /* return a pointer to the n'th column of buf */ char *column (char *buf, int n) { int i; if (n <= 0) return buf; for (i = 0; i < n; i++) { while (isspace(*buf)) buf++; if (i == n - 1) break; while ((0 != *buf) && !isspace(*buf)) buf++; } return buf; } /* column to double */ static double cold (char *args, int col) { char *endp; char *target; double d; target = column (args, col); d = proj_strtod (target, &endp); if (endp==target) return HUGE_VAL; return d; } PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time) { PJ_COORD err = proj_coord (HUGE_VAL, HUGE_VAL, HUGE_VAL, HUGE_VAL); PJ_COORD result = err; int prev_errno = errno; errno = 0; result.xyzt.z = fixed_height; result.xyzt.t = fixed_time; result.xyzt.x = cold (buf, columns[0]); result.xyzt.y = cold (buf, columns[1]); if (result.xyzt.z==HUGE_VAL) result.xyzt.z = cold (buf, columns[2]); if (result.xyzt.t==HUGE_VAL) result.xyzt.t = cold (buf, columns[3]); if (0!=errno) return err; errno = prev_errno; return result; } proj-6.3.1/src/apps/emess.h0000664000175000017500000000100313601752712012433 00000000000000/* Error message processing header file */ #ifndef EMESS_H #define EMESS_H struct EMESS { char *File_name, /* input file name */ *Prog_name; /* name of program */ int File_line; /* approximate line read where error occurred */ }; #ifdef EMESS_ROUTINE /* use type */ /* for emess procedure */ struct EMESS emess_dat = { nullptr, nullptr, 0 }; #else /* for for calling procedures */ extern struct EMESS emess_dat; #endif /* use type */ void emess(int, const char *, ...); #endif /* end EMESS_H */ proj-6.3.1/src/apps/geod.cpp0000664000175000017500000001557013601752712012606 00000000000000/* <<<< Geodesic filter program >>>> */ #include "proj.h" # include "proj_internal.h" # include "geod_interface.h" # include "emess.h" # include # include # include # define MAXLINE 200 # define MAX_PARGS 50 # define TAB putchar('\t') static int fullout = 0, /* output full set of geodesic values */ tag = '#', /* beginning of line tag character */ pos_azi = 0, /* output azimuths as positive values */ inverse = 0; /* != 0 then inverse geodesic */ static const char *oform = nullptr; /* output format for decimal degrees */ static const char *osform = "%.3f"; /* output format for S */ static char pline[50]; /* work string */ static const char *usage = "%s\nusage: %s [-afFIlptwW [args]] [+opt[=arg] ...] [file ...]\n"; static void printLL(double p, double l) { if (oform) { (void)printf(oform, p * RAD_TO_DEG); TAB; (void)printf(oform, l * RAD_TO_DEG); } else { (void)fputs(rtodms(pline, p, 'N', 'S'),stdout); TAB; (void)fputs(rtodms(pline, l, 'E', 'W'),stdout); } } static void do_arc(void) { double az; printLL(phi2, lam2); putchar('\n'); for (az = al12; n_alpha--; ) { al12 = az = adjlon(az + del_alpha); geod_pre(); geod_for(); printLL(phi2, lam2); putchar('\n'); } } static void /* generate intermediate geodesic coordinates */ do_geod(void) { double phil, laml, del_S; phil = phi2; laml = lam2; printLL(phi1, lam1); putchar('\n'); for ( geod_S = del_S = geod_S / n_S; --n_S; geod_S += del_S) { geod_for(); printLL(phi2, lam2); putchar('\n'); } printLL(phil, laml); putchar('\n'); } static void /* file processing function */ process(FILE *fid) { char line[MAXLINE+3], *s; for (;;) { ++emess_dat.File_line; if (!(s = fgets(line, MAXLINE, fid))) break; if (!strchr(s, '\n')) { /* overlong line */ int c; strcat(s, "\n"); /* gobble up to newline */ while ((c = fgetc(fid)) != EOF && c != '\n') ; } if (*s == tag) { fputs(line, stdout); continue; } phi1 = dmstor(s, &s); lam1 = dmstor(s, &s); if (inverse) { phi2 = dmstor(s, &s); lam2 = dmstor(s, &s); geod_inv(); } else { al12 = dmstor(s, &s); geod_S = strtod(s, &s) * to_meter; geod_pre(); geod_for(); } if (!*s && (s > line)) --s; /* assumed we gobbled \n */ if (pos_azi) { if (al12 < 0.) al12 += M_TWOPI; if (al21 < 0.) al21 += M_TWOPI; } if (fullout) { printLL(phi1, lam1); TAB; printLL(phi2, lam2); TAB; if (oform) { (void)printf(oform, al12 * RAD_TO_DEG); TAB; (void)printf(oform, al21 * RAD_TO_DEG); TAB; (void)printf(osform, geod_S * fr_meter); } else { (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; (void)printf(osform, geod_S * fr_meter); } } else if (inverse) if (oform) { (void)printf(oform, al12 * RAD_TO_DEG); TAB; (void)printf(oform, al21 * RAD_TO_DEG); TAB; (void)printf(osform, geod_S * fr_meter); } else { (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; (void)printf(osform, geod_S * fr_meter); } else { printLL(phi2, lam2); TAB; if (oform) (void)printf(oform, al21 * RAD_TO_DEG); else (void)fputs(rtodms(pline, al21, 0, 0), stdout); } (void)fputs(s, stdout); } } static char *pargv[MAX_PARGS]; static int pargc = 0; int main(int argc, char **argv) { char *arg, **eargv = argv; FILE *fid; static int eargc = 0, c; if ((emess_dat.Prog_name = strrchr(*argv,'/')) != nullptr) ++emess_dat.Prog_name; else emess_dat.Prog_name = *argv; inverse = ! strncmp(emess_dat.Prog_name, "inv", 3); if (argc <= 1 ) { (void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name); exit (0); } /* process run line arguments */ while (--argc > 0) { /* collect run line arguments */ if(**++argv == '-') for(arg = *argv;;) { switch(*++arg) { case '\0': /* position of "stdin" */ if (arg[-1] == '-') eargv[eargc++] = const_cast("-"); break; case 'a': /* output full set of values */ fullout = 1; continue; case 'I': /* alt. inverse spec. */ inverse = 1; continue; case 't': /* set col. one char */ if (arg[1]) tag = *++arg; else emess(1,"missing -t col. 1 tag"); continue; case 'W': /* specify seconds precision */ case 'w': /* -W for constant field width */ if ((c = arg[1]) && isdigit(c)) { set_rtodms(c - '0', *arg == 'W'); ++arg; } else emess(1,"-W argument missing or non-digit"); continue; case 'f': /* alternate output format degrees or xy */ if (--argc <= 0) noargument: emess(1,"missing argument for -%c",*arg); oform = *++argv; continue; case 'F': /* alternate output format degrees or xy */ if (--argc <= 0) goto noargument; osform = *++argv; continue; case 'l': if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ const struct PJ_ELLPS *le; for (le=proj_list_ellps(); le->id ; ++le) (void)printf("%9s %-16s %-16s %s\n", le->id, le->major, le->ell, le->name); } else if (arg[1] == 'u') { /* list of units */ const struct PJ_UNITS *lu; for (lu = proj_list_units();lu->id ; ++lu) (void)printf("%12s %-20s %s\n", lu->id, lu->to_meter, lu->name); } else emess(1,"invalid list option: l%c",arg[1]); exit( 0 ); case 'p': /* output azimuths as positive */ pos_azi = 1; continue; default: emess(1, "invalid option: -%c",*arg); break; } break; } else if (**argv == '+') /* + argument */ if (pargc < MAX_PARGS) pargv[pargc++] = *argv + 1; else emess(1,"overflowed + argument table"); else /* assumed to be input file name(s) */ eargv[eargc++] = *argv; } /* done with parameter and control input */ geod_set(pargc, pargv); /* setup projection */ if ((n_alpha || n_S) && eargc) emess(1,"files specified for arc/geodesic mode"); if (n_alpha) do_arc(); else if (n_S) do_geod(); else { /* process input file list */ if (eargc == 0) /* if no specific files force sysin */ eargv[eargc++] = const_cast("-"); for ( ; eargc-- ; ++eargv) { if (**eargv == '-') { fid = stdin; emess_dat.File_name = const_cast(""); } else { if ((fid = fopen(*eargv, "r")) == nullptr) { emess(-2, *eargv, "input file"); continue; } emess_dat.File_name = *eargv; } emess_dat.File_line = 0; process(fid); (void)fclose(fid); emess_dat.File_name = (char *)nullptr; } } exit(0); /* normal completion */ } proj-6.3.1/src/apps/projinfo.cpp0000664000175000017500000014317313617003642013515 00000000000000/****************************************************************************** * * Project: PROJ * Purpose: projinfo utility * Author: Even Rouault * ****************************************************************************** * Copyright (c) 2018, 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. ****************************************************************************/ //! @cond Doxygen_Suppress #define FROM_PROJ_CPP #include #include // std::ifstream #include #include #include "proj.h" #include "proj_internal.h" #include #include #include #include #include #include #include "proj/internal/internal.hpp" // for split using namespace NS_PROJ::common; using namespace NS_PROJ::crs; using namespace NS_PROJ::io; using namespace NS_PROJ::metadata; using namespace NS_PROJ::operation; using namespace NS_PROJ::util; using namespace NS_PROJ::internal; // --------------------------------------------------------------------------- namespace { // anonymous namespace struct OutputOptions { bool quiet = false; bool PROJ5 = false; bool WKT2_2019 = false; bool WKT2_2019_SIMPLIFIED = false; bool WKT2_2015 = false; bool WKT2_2015_SIMPLIFIED = false; bool WKT1_GDAL = false; bool WKT1_ESRI = false; bool PROJJSON = false; bool c_ify = false; bool singleLine = false; bool strict = true; }; } // anonymous namespace // --------------------------------------------------------------------------- static void usage() { std::cerr << "usage: projinfo [-o formats] [-k crs|operation|ellipsoid] " "[--summary] [-q]" << std::endl << " ([--area name_or_code] | " "[--bbox west_long,south_lat,east_long,north_lat]) " << std::endl << " [--spatial-test contains|intersects]" << std::endl << " [--crs-extent-use none|both|intersection|smallest]" << std::endl << " [--grid-check none|discard_missing|sort] " "[--show-superseded]" << std::endl << " [--pivot-crs always|if_no_direct_transformation|" << "never|{auth:code[,auth:code]*}]" << std::endl << " [--boundcrs-to-wgs84]" << std::endl << " [--main-db-path path] [--aux-db-path path]*" << std::endl << " [--identify] [--3d]" << std::endl << " [--c-ify] [--single-line]" << std::endl << " {object_definition} | (-s {srs_def} -t {srs_def})" << std::endl; std::cerr << std::endl; std::cerr << "-o: formats is a comma separated combination of: " "all,default,PROJ,WKT_ALL,WKT2:2015,WKT2:2019,WKT1:GDAL," "WKT1:ESRI,PROJJSON" << std::endl; std::cerr << " Except 'all' and 'default', other format can be preceded " "by '-' to disable them" << std::endl; std::cerr << std::endl; std::cerr << "{object_definition} might be a PROJ string, a WKT string, " " a AUTHORITY:CODE, or urn:ogc:def:OBJECT_TYPE:AUTHORITY::CODE" << std::endl; std::exit(1); } // --------------------------------------------------------------------------- static std::string un_c_ify_string(const std::string &str) { std::string out(str); out = out.substr(1, out.size() - 2); out = replaceAll(out, "\\\"", "{ESCAPED_DOUBLE_QUOTE}"); out = replaceAll(out, "\\n\"", ""); out = replaceAll(out, "\"", ""); out = replaceAll(out, "{ESCAPED_DOUBLE_QUOTE}", "\""); return out; } // --------------------------------------------------------------------------- static std::string c_ify_string(const std::string &str) { std::string out(str); out = replaceAll(out, "\"", "{DOUBLE_QUOTE}"); out = replaceAll(out, "\n", "\\n\"\n\""); out = replaceAll(out, "{DOUBLE_QUOTE}", "\\\""); return "\"" + out + "\""; } // --------------------------------------------------------------------------- static BaseObjectNNPtr buildObject( DatabaseContextPtr dbContext, const std::string &user_string, const std::string &kind, const std::string &context, bool buildBoundCRSToWGS84, CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS, bool promoteTo3D, bool quiet) { BaseObjectPtr obj; std::string l_user_string(user_string); if (!user_string.empty() && user_string[0] == '@') { std::ifstream fs; auto filename = user_string.substr(1); fs.open(filename, std::fstream::in | std::fstream::binary); if (!fs.is_open()) { std::cerr << context << ": cannot open " << filename << std::endl; std::exit(1); } l_user_string.clear(); while (!fs.eof()) { char buffer[256]; fs.read(buffer, sizeof(buffer)); l_user_string.append(buffer, static_cast(fs.gcount())); if (l_user_string.size() > 100 * 1000) { fs.close(); std::cerr << context << ": too big file" << std::endl; std::exit(1); } } fs.close(); } if (!l_user_string.empty() && l_user_string.back() == '\n') { l_user_string.resize(l_user_string.size() - 1); } if (!l_user_string.empty() && l_user_string.back() == '\r') { l_user_string.resize(l_user_string.size() - 1); } try { auto tokens = split(l_user_string, ':'); if (kind == "operation" && tokens.size() == 2) { auto urn = "urn:ogc:def:coordinateOperation:" + tokens[0] + "::" + tokens[1]; obj = createFromUserInput(urn, dbContext).as_nullable(); } else if (kind == "ellipsoid" && tokens.size() == 2) { auto urn = "urn:ogc:def:ellipsoid:" + tokens[0] + "::" + tokens[1]; obj = createFromUserInput(urn, dbContext).as_nullable(); } else { // Convenience to be able to use C escaped strings... if (l_user_string.size() > 2 && l_user_string[0] == '"' && l_user_string.back() == '"' && l_user_string.find("\\\"") != std::string::npos) { l_user_string = un_c_ify_string(l_user_string); } WKTParser wktParser; if (wktParser.guessDialect(l_user_string) != WKTParser::WKTGuessedDialect::NOT_WKT) { wktParser.setStrict(false); wktParser.attachDatabaseContext(dbContext); obj = wktParser.createFromWKT(l_user_string).as_nullable(); if (!quiet) { auto warnings = wktParser.warningList(); if (!warnings.empty()) { for (const auto &str : warnings) { std::cerr << "Warning: " << str << std::endl; } } } } else { obj = createFromUserInput(l_user_string, dbContext).as_nullable(); } } } catch (const std::exception &e) { std::cerr << context << ": parsing of user string failed: " << e.what() << std::endl; std::exit(1); } if (buildBoundCRSToWGS84) { auto crs = std::dynamic_pointer_cast(obj); if (crs) { obj = crs->createBoundCRSToWGS84IfPossible(dbContext, allowUseIntermediateCRS) .as_nullable(); } } if (promoteTo3D) { auto crs = std::dynamic_pointer_cast(obj); if (crs) { obj = crs->promoteTo3D(std::string(), dbContext).as_nullable(); } } return NN_NO_CHECK(obj); } // --------------------------------------------------------------------------- static void outputObject( DatabaseContextPtr dbContext, BaseObjectNNPtr obj, CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS, const OutputOptions &outputOpt) { auto identified = dynamic_cast(obj.get()); if (!outputOpt.quiet && identified && identified->isDeprecated()) { std::cout << "Warning: object is deprecated" << std::endl; auto crs = dynamic_cast(obj.get()); if (crs && dbContext) { try { auto list = crs->getNonDeprecated(NN_NO_CHECK(dbContext)); if (!list.empty()) { std::cout << "Alternative non-deprecated CRS:" << std::endl; } for (const auto &altCRS : list) { const auto &ids = altCRS->identifiers(); if (!ids.empty()) { std::cout << " " << *(ids[0]->codeSpace()) << ":" << ids[0]->code() << std::endl; } } } catch (const std::exception &) { } } std::cout << std::endl; } auto projStringExportable = nn_dynamic_pointer_cast(obj); bool alreadyOutputed = false; if (projStringExportable) { if (outputOpt.PROJ5) { try { if (alreadyOutputed) { std::cout << std::endl; } auto crs = nn_dynamic_pointer_cast(obj); if (!outputOpt.quiet) { if (crs) { std::cout << "PROJ.4 string:" << std::endl; } else { std::cout << "PROJ string:" << std::endl; } } std::shared_ptr objToExport; if (crs) { objToExport = nn_dynamic_pointer_cast( crs->createBoundCRSToWGS84IfPossible( dbContext, allowUseIntermediateCRS)); } if (!objToExport) { objToExport = projStringExportable; } std::cout << objToExport->exportToPROJString( PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, dbContext) .get()) << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to PROJ string: " << e.what() << std::endl; } alreadyOutputed = true; } } auto wktExportable = nn_dynamic_pointer_cast(obj); if (wktExportable) { if (outputOpt.WKT2_2015) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT2:2015 string:" << std::endl; } auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2015); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT2:2015: " << e.what() << std::endl; } alreadyOutputed = true; } if (outputOpt.WKT2_2015_SIMPLIFIED) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT2:2015_SIMPLIFIED string:" << std::endl; } auto formatter = WKTFormatter::create( WKTFormatter::Convention::WKT2_2015_SIMPLIFIED); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT2:2015_SIMPLIFIED: " << e.what() << std::endl; } alreadyOutputed = true; } if (outputOpt.WKT2_2019) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT2:2019 string:" << std::endl; } auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT2_2019); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT2:2019: " << e.what() << std::endl; } alreadyOutputed = true; } if (outputOpt.WKT2_2019_SIMPLIFIED) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT2:2019_SIMPLIFIED string:" << std::endl; } auto formatter = WKTFormatter::create( WKTFormatter::Convention::WKT2_2019_SIMPLIFIED); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT2:2019_SIMPLIFIED: " << e.what() << std::endl; } alreadyOutputed = true; } if (outputOpt.WKT1_GDAL && !nn_dynamic_pointer_cast(obj)) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT1:GDAL string:" << std::endl; } auto formatter = WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL); if (outputOpt.singleLine) { formatter->setMultiLine(false); } formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; std::cout << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT1:GDAL: " << e.what() << std::endl; } alreadyOutputed = true; } if (outputOpt.WKT1_ESRI && !nn_dynamic_pointer_cast(obj)) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "WKT1:ESRI string:" << std::endl; } auto formatter = WKTFormatter::create( WKTFormatter::Convention::WKT1_ESRI, dbContext); formatter->setStrict(outputOpt.strict); auto wkt = wktExportable->exportToWKT(formatter.get()); if (outputOpt.c_ify) { wkt = c_ify_string(wkt); } std::cout << wkt << std::endl; std::cout << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to WKT1:ESRI: " << e.what() << std::endl; } alreadyOutputed = true; } } auto JSONExportable = nn_dynamic_pointer_cast(obj); if (JSONExportable) { if (outputOpt.PROJJSON) { try { if (alreadyOutputed) { std::cout << std::endl; } if (!outputOpt.quiet) { std::cout << "PROJJSON:" << std::endl; } auto formatter(JSONFormatter::create(dbContext)); if (outputOpt.singleLine) { formatter->setMultiLine(false); } auto jsonString(JSONExportable->exportToJSON(formatter.get())); if (outputOpt.c_ify) { jsonString = c_ify_string(jsonString); } std::cout << jsonString << std::endl; } catch (const std::exception &e) { std::cerr << "Error when exporting to PROJJSON: " << e.what() << std::endl; } // alreadyOutputed = true; } } auto op = dynamic_cast(obj.get()); if (op && dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) { try { auto setGrids = op->gridsNeeded(dbContext); bool firstWarning = true; for (const auto &grid : setGrids) { if (!grid.available) { if (firstWarning) { std::cout << std::endl; firstWarning = false; } std::cout << "Grid " << grid.shortName << " needed but not found on the system."; if (!grid.packageName.empty()) { std::cout << " Can be obtained from the " << grid.packageName << " package"; if (!grid.url.empty()) { std::cout << " at " << grid.url; } } else if (!grid.url.empty()) { std::cout << " Can be obtained at " << grid.url; } std::cout << std::endl; } } } catch (const std::exception &e) { std::cerr << "Error in gridsNeeded(): " << e.what() << std::endl; } } } // --------------------------------------------------------------------------- static void outputOperationSummary(const CoordinateOperationNNPtr &op, const DatabaseContextPtr &dbContext) { auto ids = op->identifiers(); if (!ids.empty()) { std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code(); } else { std::cout << "unknown id"; } std::cout << ", "; auto name = op->nameStr(); if (!name.empty()) { std::cout << name; } else { std::cout << "unknown name"; } std::cout << ", "; auto accuracies = op->coordinateOperationAccuracies(); if (!accuracies.empty()) { std::cout << accuracies[0]->value() << " m"; } else { if (std::dynamic_pointer_cast(op.as_nullable())) { std::cout << "0 m"; } else { std::cout << "unknown accuracy"; } } std::cout << ", "; auto domains = op->domains(); if (!domains.empty() && domains[0]->domainOfValidity() && domains[0]->domainOfValidity()->description().has_value()) { std::cout << *(domains[0]->domainOfValidity()->description()); } else { std::cout << "unknown domain of validity"; } if (op->hasBallparkTransformation()) { std::cout << ", has ballpark transformation"; } if (dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) { try { auto setGrids = op->gridsNeeded(dbContext); for (const auto &grid : setGrids) { if (!grid.available) { std::cout << ", at least one grid missing"; break; } } } catch (const std::exception &) { } } std::cout << std::endl; } // --------------------------------------------------------------------------- static void outputOperations( DatabaseContextPtr dbContext, const std::string &sourceCRSStr, const std::string &targetCRSStr, const ExtentPtr &bboxFilter, CoordinateOperationContext::SpatialCriterion spatialCriterion, bool spatialCriterionExplicitlySpecified, CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse, CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse, CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS, const std::vector> &pivots, const std::string &authority, bool usePROJGridAlternatives, bool showSuperseded, bool promoteTo3D, const OutputOptions &outputOpt, bool summary) { auto sourceObj = buildObject(dbContext, sourceCRSStr, "crs", "source CRS", false, CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, outputOpt.quiet); auto sourceCRS = nn_dynamic_pointer_cast(sourceObj); if (!sourceCRS) { std::cerr << "source CRS string is not a CRS" << std::endl; std::exit(1); } auto nnSourceCRS = NN_NO_CHECK(sourceCRS); auto targetObj = buildObject(dbContext, targetCRSStr, "crs", "target CRS", false, CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, outputOpt.quiet); auto targetCRS = nn_dynamic_pointer_cast(targetObj); if (!targetCRS) { std::cerr << "target CRS string is not a CRS" << std::endl; std::exit(1); } auto nnTargetCRS = NN_NO_CHECK(targetCRS); std::vector list; size_t spatialCriterionPartialIntersectionResultCount = 0; try { auto authFactory = dbContext ? AuthorityFactory::create(NN_NO_CHECK(dbContext), authority) .as_nullable() : nullptr; auto ctxt = CoordinateOperationContext::create(authFactory, bboxFilter, 0); ctxt->setSpatialCriterion(spatialCriterion); ctxt->setSourceAndTargetCRSExtentUse(crsExtentUse); ctxt->setGridAvailabilityUse(gridAvailabilityUse); ctxt->setAllowUseIntermediateCRS(allowUseIntermediateCRS); ctxt->setIntermediateCRS(pivots); ctxt->setUsePROJAlternativeGridNames(usePROJGridAlternatives); ctxt->setDiscardSuperseded(!showSuperseded); list = CoordinateOperationFactory::create()->createOperations( nnSourceCRS, nnTargetCRS, ctxt); if (!spatialCriterionExplicitlySpecified && spatialCriterion == CoordinateOperationContext::SpatialCriterion:: STRICT_CONTAINMENT) { try { ctxt->setSpatialCriterion( CoordinateOperationContext::SpatialCriterion:: PARTIAL_INTERSECTION); spatialCriterionPartialIntersectionResultCount = CoordinateOperationFactory::create() ->createOperations(nnSourceCRS, nnTargetCRS, ctxt) .size(); } catch (const std::exception &) { } } } catch (const std::exception &e) { std::cerr << "createOperations() failed with: " << e.what() << std::endl; std::exit(1); } if (outputOpt.quiet && !list.empty()) { outputObject(dbContext, list[0], allowUseIntermediateCRS, outputOpt); return; } std::cout << "Candidate operations found: " << list.size() << std::endl; if (spatialCriterionPartialIntersectionResultCount > list.size()) { std::cout << "Note: using '--spatial-test intersects' would bring " "more results (" << spatialCriterionPartialIntersectionResultCount << ")" << std::endl; } if (summary) { for (const auto &op : list) { outputOperationSummary(op, dbContext); } } else { bool first = true; for (size_t i = 0; i < list.size(); ++i) { const auto &op = list[i]; if (!first) { std::cout << std::endl; } first = false; std::cout << "-------------------------------------" << std::endl; std::cout << "Operation No. " << (i + 1) << ":" << std::endl << std::endl; outputOperationSummary(op, dbContext); std::cout << std::endl; outputObject(dbContext, op, allowUseIntermediateCRS, outputOpt); } } } // --------------------------------------------------------------------------- int main(int argc, char **argv) { if (argc == 1) { std::cerr << pj_get_release() << std::endl; usage(); } std::string user_string; bool user_string_specified = false; std::string sourceCRSStr; std::string targetCRSStr; bool outputSwithSpecified = false; OutputOptions outputOpt; std::string objectKind; bool summary = false; ExtentPtr bboxFilter = nullptr; std::string area; bool spatialCriterionExplicitlySpecified = false; CoordinateOperationContext::SpatialCriterion spatialCriterion = CoordinateOperationContext::SpatialCriterion::STRICT_CONTAINMENT; CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse = CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST; bool buildBoundCRSToWGS84 = false; CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse = CoordinateOperationContext::GridAvailabilityUse::USE_FOR_SORTING; CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS = CoordinateOperationContext::IntermediateCRSUse:: IF_NO_DIRECT_TRANSFORMATION; std::vector> pivots; bool usePROJGridAlternatives = true; std::string mainDBPath; std::vector auxDBPath; bool guessDialect = false; std::string authority; bool identify = false; bool showSuperseded = false; bool promoteTo3D = false; for (int i = 1; i < argc; i++) { std::string arg(argv[i]); if (arg == "-o" && i + 1 < argc) { outputSwithSpecified = true; i++; auto formats(split(argv[i], ',')); for (auto format : formats) { if (ci_equal(format, "all")) { outputOpt.PROJ5 = true; outputOpt.WKT2_2019 = true; outputOpt.WKT2_2015 = true; outputOpt.WKT1_GDAL = true; outputOpt.WKT1_ESRI = true; outputOpt.PROJJSON = true; } else if (ci_equal(format, "default")) { outputOpt.PROJ5 = true; outputOpt.WKT2_2019 = true; outputOpt.WKT2_2015 = false; outputOpt.WKT1_GDAL = false; } else if (ci_equal(format, "PROJ")) { outputOpt.PROJ5 = true; } else if (ci_equal(format, "-PROJ")) { outputOpt.PROJ5 = false; } else if (ci_equal(format, "WKT_ALL") || ci_equal(format, "WKT-ALL")) { outputOpt.WKT2_2019 = true; outputOpt.WKT2_2015 = true; outputOpt.WKT1_GDAL = true; } else if (ci_equal(format, "-WKT_ALL") || ci_equal(format, "-WKT-ALL")) { outputOpt.WKT2_2019 = false; outputOpt.WKT2_2015 = false; outputOpt.WKT1_GDAL = false; } else if (ci_equal(format, "WKT2_2019") || ci_equal(format, "WKT2-2019") || ci_equal(format, "WKT2:2019") || /* legacy: undocumented */ ci_equal(format, "WKT2_2018") || ci_equal(format, "WKT2-2018") || ci_equal(format, "WKT2:2018")) { outputOpt.WKT2_2019 = true; } else if (ci_equal(format, "WKT2_2019_SIMPLIFIED") || ci_equal(format, "WKT2-2019_SIMPLIFIED") || ci_equal(format, "WKT2:2019_SIMPLIFIED") || /* legacy: undocumented */ ci_equal(format, "WKT2_2018_SIMPLIFIED") || ci_equal(format, "WKT2-2018_SIMPLIFIED") || ci_equal(format, "WKT2:2018_SIMPLIFIED")) { outputOpt.WKT2_2019_SIMPLIFIED = true; } else if (ci_equal(format, "-WKT2_2019") || ci_equal(format, "-WKT2-2019") || ci_equal(format, "-WKT2:2019") || /* legacy: undocumented */ ci_equal(format, "-WKT2_2018") || ci_equal(format, "-WKT2-2018") || ci_equal(format, "-WKT2:2018")) { outputOpt.WKT2_2019 = false; } else if (ci_equal(format, "WKT2_2015") || ci_equal(format, "WKT2-2015") || ci_equal(format, "WKT2:2015")) { outputOpt.WKT2_2015 = true; } else if (ci_equal(format, "WKT2_2015_SIMPLIFIED") || ci_equal(format, "WKT2-2015_SIMPLIFIED") || ci_equal(format, "WKT2:2015_SIMPLIFIED")) { outputOpt.WKT2_2015_SIMPLIFIED = true; } else if (ci_equal(format, "-WKT2_2015") || ci_equal(format, "-WKT2-2015") || ci_equal(format, "-WKT2:2015")) { outputOpt.WKT2_2015 = false; } else if (ci_equal(format, "WKT1_GDAL") || ci_equal(format, "WKT1-GDAL") || ci_equal(format, "WKT1:GDAL")) { outputOpt.WKT1_GDAL = true; } else if (ci_equal(format, "-WKT1_GDAL") || ci_equal(format, "-WKT1-GDAL") || ci_equal(format, "-WKT1:GDAL")) { outputOpt.WKT1_GDAL = false; } else if (ci_equal(format, "WKT1_ESRI") || ci_equal(format, "WKT1-ESRI") || ci_equal(format, "WKT1:ESRI")) { outputOpt.WKT1_ESRI = true; } else if (ci_equal(format, "-WKT1_ESRI") || ci_equal(format, "-WKT1-ESRI") || ci_equal(format, "-WKT1:ESRI")) { outputOpt.WKT1_ESRI = false; } else if (ci_equal(format, "PROJJSON")) { outputOpt.PROJJSON = true; } else if (ci_equal(format, "-PROJJSON")) { outputOpt.PROJJSON = false; } else { std::cerr << "Unrecognized value for option -o: " << format << std::endl; usage(); } } } else if (arg == "--bbox" && i + 1 < argc) { i++; auto bboxStr(argv[i]); auto bbox(split(bboxStr, ',')); if (bbox.size() != 4) { std::cerr << "Incorrect number of values for option --bbox: " << bboxStr << std::endl; usage(); } try { bboxFilter = Extent::createFromBBOX( c_locale_stod(bbox[0]), c_locale_stod(bbox[1]), c_locale_stod(bbox[2]), c_locale_stod(bbox[3])) .as_nullable(); } catch (const std::exception &e) { std::cerr << "Invalid value for option --bbox: " << bboxStr << ", " << e.what() << std::endl; usage(); } } else if (arg == "--area" && i + 1 < argc) { i++; area = argv[i]; } else if (arg == "-k" && i + 1 < argc) { i++; std::string kind(argv[i]); if (ci_equal(kind, "crs") || ci_equal(kind, "srs")) { objectKind = "crs"; } else if (ci_equal(kind, "operation")) { objectKind = "operation"; } else if (ci_equal(kind, "ellipsoid")) { objectKind = "ellipsoid"; } else { std::cerr << "Unrecognized value for option -k: " << kind << std::endl; usage(); } } else if ((arg == "-s" || arg == "--ssource-crs") && i + 1 < argc) { i++; sourceCRSStr = argv[i]; } else if ((arg == "-t" || arg == "--target-crs") && i + 1 < argc) { i++; targetCRSStr = argv[i]; } else if (arg == "-q" || arg == "--quiet") { outputOpt.quiet = true; } else if (arg == "--c-ify") { outputOpt.c_ify = true; } else if (arg == "--single-line") { outputOpt.singleLine = true; } else if (arg == "--summary") { summary = true; } else if (ci_equal(arg, "--boundcrs-to-wgs84")) { buildBoundCRSToWGS84 = true; // undocumented: only for debugging purposes } else if (ci_equal(arg, "--no-proj-grid-alternatives")) { usePROJGridAlternatives = false; } else if (arg == "--spatial-test" && i + 1 < argc) { i++; std::string value(argv[i]); spatialCriterionExplicitlySpecified = true; if (ci_equal(value, "contains")) { spatialCriterion = CoordinateOperationContext:: SpatialCriterion::STRICT_CONTAINMENT; } else if (ci_equal(value, "intersects")) { spatialCriterion = CoordinateOperationContext:: SpatialCriterion::PARTIAL_INTERSECTION; } else { std::cerr << "Unrecognized value for option --spatial-test: " << value << std::endl; usage(); } } else if (arg == "--crs-extent-use" && i + 1 < argc) { i++; std::string value(argv[i]); if (ci_equal(value, "NONE")) { crsExtentUse = CoordinateOperationContext::SourceTargetCRSExtentUse::NONE; } else if (ci_equal(value, "BOTH")) { crsExtentUse = CoordinateOperationContext::SourceTargetCRSExtentUse::BOTH; } else if (ci_equal(value, "INTERSECTION")) { crsExtentUse = CoordinateOperationContext:: SourceTargetCRSExtentUse::INTERSECTION; } else if (ci_equal(value, "SMALLEST")) { crsExtentUse = CoordinateOperationContext:: SourceTargetCRSExtentUse::SMALLEST; } else { std::cerr << "Unrecognized value for option --crs-extent-use: " << value << std::endl; usage(); } } else if (arg == "--grid-check" && i + 1 < argc) { i++; std::string value(argv[i]); if (ci_equal(value, "none")) { gridAvailabilityUse = CoordinateOperationContext:: GridAvailabilityUse::IGNORE_GRID_AVAILABILITY; } else if (ci_equal(value, "discard_missing")) { gridAvailabilityUse = CoordinateOperationContext:: GridAvailabilityUse::DISCARD_OPERATION_IF_MISSING_GRID; } else if (ci_equal(value, "sort")) { gridAvailabilityUse = CoordinateOperationContext:: GridAvailabilityUse::USE_FOR_SORTING; } else { std::cerr << "Unrecognized value for option --grid-check: " << value << std::endl; usage(); } } else if (arg == "--pivot-crs" && i + 1 < argc) { i++; auto value(argv[i]); if (ci_equal(std::string(value), "always")) { allowUseIntermediateCRS = CoordinateOperationContext::IntermediateCRSUse::ALWAYS; } else if (ci_equal(std::string(value), "if_no_direct_transformation")) { allowUseIntermediateCRS = CoordinateOperationContext:: IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION; } else if (ci_equal(std::string(value), "never")) { allowUseIntermediateCRS = CoordinateOperationContext::IntermediateCRSUse::NEVER; } else { auto splitValue(split(value, ',')); for (const auto &v : splitValue) { auto auth_code = split(v, ':'); if (auth_code.size() != 2) { std::cerr << "Unrecognized value for option --grid-check: " << value << std::endl; usage(); } pivots.emplace_back( std::make_pair(auth_code[0], auth_code[1])); } } } else if (arg == "--main-db-path" && i + 1 < argc) { i++; mainDBPath = argv[i]; } else if (arg == "--aux-db-path" && i + 1 < argc) { i++; auxDBPath.push_back(argv[i]); } else if (arg == "--guess-dialect") { guessDialect = true; } else if (arg == "--authority" && i + 1 < argc) { i++; authority = argv[i]; } else if (arg == "--identify") { identify = true; } else if (arg == "--show-superseded") { showSuperseded = true; } else if (arg == "--lax") { outputOpt.strict = false; } else if (ci_equal(arg, "--3d")) { promoteTo3D = true; } else if (arg == "-?" || arg == "--help") { usage(); } else if (arg[0] == '-') { std::cerr << "Unrecognized option: " << arg << std::endl; usage(); } else { if (!user_string_specified) { user_string_specified = true; user_string = arg; } else { std::cerr << "Too many parameters: " << arg << std::endl; usage(); } } } if (bboxFilter && !area.empty()) { std::cerr << "ERROR: --bbox and --area are exclusive" << std::endl; std::exit(1); } DatabaseContextPtr dbContext; try { dbContext = DatabaseContext::create(mainDBPath, auxDBPath).as_nullable(); } catch (const std::exception &e) { if (!mainDBPath.empty() || !auxDBPath.empty() || !area.empty()) { std::cerr << "ERROR: Cannot create database connection: " << e.what() << std::endl; std::exit(1); } std::cerr << "WARNING: Cannot create database connection: " << e.what() << std::endl; } if (!sourceCRSStr.empty() && targetCRSStr.empty()) { std::cerr << "Source CRS specified, but missing target CRS" << std::endl; usage(); } else if (sourceCRSStr.empty() && !targetCRSStr.empty()) { std::cerr << "Target CRS specified, but missing source CRS" << std::endl; usage(); } else if (!sourceCRSStr.empty() && !targetCRSStr.empty()) { if (user_string_specified) { std::cerr << "Unused extra value" << std::endl; usage(); } } else if (!user_string_specified) { std::cerr << "Missing user string" << std::endl; usage(); } if (!outputSwithSpecified) { outputOpt.PROJ5 = true; outputOpt.WKT2_2019 = true; } if (outputOpt.quiet && (outputOpt.PROJ5 + outputOpt.WKT2_2019 + outputOpt.WKT2_2015 + outputOpt.WKT1_GDAL + outputOpt.PROJJSON) != 1) { std::cerr << "-q can only be used with a single output format" << std::endl; usage(); } if (!user_string.empty()) { try { auto obj(buildObject(dbContext, user_string, objectKind, "input string", buildBoundCRSToWGS84, allowUseIntermediateCRS, promoteTo3D, outputOpt.quiet)); if (guessDialect) { auto dialect = WKTParser().guessDialect(user_string); std::cout << "Guessed WKT dialect: "; if (dialect == WKTParser::WKTGuessedDialect::WKT2_2019) { std::cout << "WKT2_2019"; } else if (dialect == WKTParser::WKTGuessedDialect::WKT2_2015) { std::cout << "WKT2_2015"; } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_GDAL) { std::cout << "WKT1_GDAL"; } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_ESRI) { std::cout << "WKT1_ESRI"; } else { std::cout << "Not WKT / unknown"; } std::cout << std::endl; } outputObject(dbContext, obj, allowUseIntermediateCRS, outputOpt); if (identify) { auto crs = dynamic_cast(obj.get()); if (crs) { try { auto res = crs->identify( dbContext ? AuthorityFactory::create( NN_NO_CHECK(dbContext), authority) .as_nullable() : nullptr); std::cout << std::endl; std::cout << "Identification match count: " << res.size() << std::endl; for (const auto &pair : res) { const auto &identifiedCRS = pair.first; const auto &ids = identifiedCRS->identifiers(); if (!ids.empty()) { std::cout << *ids[0]->codeSpace() << ":" << ids[0]->code() << ": " << pair.second << " %" << std::endl; } else { auto boundCRS = dynamic_cast( identifiedCRS.get()); if (boundCRS && !boundCRS->baseCRS() ->identifiers() .empty()) { const auto &idsBase = boundCRS->baseCRS()->identifiers(); std::cout << "BoundCRS of " << *idsBase[0]->codeSpace() << ":" << idsBase[0]->code() << ": " << pair.second << " %" << std::endl; } else { std::cout << "un-identifier CRS: " << pair.second << " %" << std::endl; } } } } catch (const std::exception &e) { std::cerr << "Identification failed: " << e.what() << std::endl; } } } } catch (const std::exception &e) { std::cerr << "buildObject failed: " << e.what() << std::endl; std::exit(1); } } else { if (!area.empty()) { assert(dbContext); try { if (area.find(' ') == std::string::npos && area.find(':') != std::string::npos) { auto tokens = split(area, ':'); if (tokens.size() == 2) { const std::string &areaAuth = tokens[0]; const std::string &areaCode = tokens[1]; bboxFilter = AuthorityFactory::create( NN_NO_CHECK(dbContext), areaAuth) ->createExtent(areaCode) .as_nullable(); } } if (!bboxFilter) { auto authFactory = AuthorityFactory::create( NN_NO_CHECK(dbContext), std::string()); auto res = authFactory->listAreaOfUseFromName(area, false); if (res.size() == 1) { bboxFilter = AuthorityFactory::create(NN_NO_CHECK(dbContext), res.front().first) ->createExtent(res.front().second) .as_nullable(); } else { res = authFactory->listAreaOfUseFromName(area, true); if (res.size() == 1) { bboxFilter = AuthorityFactory::create(NN_NO_CHECK(dbContext), res.front().first) ->createExtent(res.front().second) .as_nullable(); } else if (res.empty()) { std::cerr << "No area of use matching provided name" << std::endl; std::exit(1); } else { std::cerr << "Several candidates area of use " "matching provided name :" << std::endl; for (const auto &candidate : res) { auto obj = AuthorityFactory::create( NN_NO_CHECK(dbContext), candidate.first) ->createExtent(candidate.second); std::cerr << " " << candidate.first << ":" << candidate.second << " : " << *obj->description() << std::endl; } std::exit(1); } } } } catch (const std::exception &e) { std::cerr << "Area of use retrieval failed: " << e.what() << std::endl; std::exit(1); } } try { outputOperations(dbContext, sourceCRSStr, targetCRSStr, bboxFilter, spatialCriterion, spatialCriterionExplicitlySpecified, crsExtentUse, gridAvailabilityUse, allowUseIntermediateCRS, pivots, authority, usePROJGridAlternatives, showSuperseded, promoteTo3D, outputOpt, summary); } catch (const std::exception &e) { std::cerr << "outputOperations() failed with: " << e.what() << std::endl; std::exit(1); } } return 0; } //! @endcond proj-6.3.1/src/apps/geod_interface.h0000664000175000017500000000162713601752712014271 00000000000000#if !defined(GEOD_INTERFACE_H) #define GEOD_INTERFACE_H #include "geodesic.h" #ifdef __cplusplus extern "C" { #endif #ifndef _IN_GEOD_SET # define GEOD_EXTERN extern #else # define GEOD_EXTERN #endif GEOD_EXTERN struct geodesic { double A, FLAT, LAM1, PHI1, ALPHA12, LAM2, PHI2, ALPHA21, DIST; } GEODESIC; # define geod_a GEODESIC.A # define geod_f GEODESIC.FLAT # define lam1 GEODESIC.LAM1 # define phi1 GEODESIC.PHI1 # define al12 GEODESIC.ALPHA12 # define lam2 GEODESIC.LAM2 # define phi2 GEODESIC.PHI2 # define al21 GEODESIC.ALPHA21 # define geod_S GEODESIC.DIST GEOD_EXTERN struct geod_geodesic GlobalGeodesic; GEOD_EXTERN struct geod_geodesicline GlobalGeodesicLine; GEOD_EXTERN int n_alpha, n_S; GEOD_EXTERN double to_meter, fr_meter, del_alpha; void geod_set(int, char **); void geod_ini(void); void geod_pre(void); void geod_for(void); void geod_inv(void); #ifdef __cplusplus } #endif #endif proj-6.3.1/src/apps/optargpm.h0000664000175000017500000005232513601752712013165 00000000000000/*********************************************************************** OPTARGPM - a header-only library for decoding PROJ.4 style command line options Thomas Knudsen, 2017-09-10 ************************************************************************ For PROJ.4 command line programs, we have a somewhat complex option decoding situation, since we have to navigate in a cocktail of classic single letter style options, prefixed by "-", GNU style long options prefixed by "--", transformation specification elements prefixed by "+", and input file names prefixed by "" (i.e. nothing). Hence, classic getopt.h style decoding does not cut the mustard, so this is an attempt to catch up and chop the ketchup. Since optargpm (for "optarg plus minus") does not belong, in any obvious way, in any systems development library, it is provided as a "header only" library. While this is conventional in C++, it is frowned at in plain C. But frown away - "header only" has its places, and this is one of them. By convention, we expect a command line to consist of the following elements: [short ("-")/long ("--") options} [operator ("+") specs] [operands/input files] or less verbose: [options] [operator specs] [operands] or less abstract: proj -I --output=foo +proj=utm +zone=32 +ellps=GRS80 bar baz... Where Operator is proj Options are -I --output=foo Operator specs are +proj=utm +zone=32 +ellps=GRS80 Operands are bar baz While neither claiming to save the world, nor to hint at the "shape of jazz to come", at least optargpm has shown useful in constructing cs2cs style transformation filters. Supporting a wide range of option syntax, the getoptpm API is somewhat quirky, but also compact, consisting of one data type, 3(+2) functions, and one enumeration: OPTARGS Housekeeping data type. An instance of OPTARGS is conventionally called o or opt opt_parse (opt, argc, argv ...): The work horse: Define supported options; Split (argc, argv) into groups (options, op specs, operands); Parse option arguments. opt_given (o, option): The number of times