aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/4D_api.cpp121
-rw-r--r--src/Makefile.am8
-rw-r--r--src/apps/cct.cpp42
-rw-r--r--src/apps/cs2cs.cpp13
-rw-r--r--src/apps/gie.cpp1
-rw-r--r--src/apps/proj.cpp19
-rw-r--r--src/apps/projinfo.cpp77
-rw-r--r--src/conversions/unitconvert.cpp15
-rw-r--r--src/ell_set.cpp4
-rw-r--r--src/filemanager.hpp3
-rw-r--r--src/geodesic.h2
-rw-r--r--src/grids.cpp18
-rw-r--r--src/iso19111/c_api.cpp112
-rw-r--r--src/iso19111/common.cpp33
-rw-r--r--src/iso19111/coordinateoperation.cpp133
-rw-r--r--src/iso19111/coordinatesystem.cpp27
-rw-r--r--src/iso19111/crs.cpp422
-rw-r--r--src/iso19111/datum.cpp151
-rw-r--r--src/iso19111/factory.cpp337
-rw-r--r--src/iso19111/io.cpp53
-rw-r--r--src/iso19111/metadata.cpp21
-rw-r--r--src/iso19111/util.cpp8
-rw-r--r--src/jniproj.cpp469
-rw-r--r--src/lib_proj.cmake23
-rw-r--r--src/networkfilemanager.cpp3
-rw-r--r--src/org_proj4_PJ.h135
-rw-r--r--src/pipeline.cpp18
-rw-r--r--src/proj.h11
-rw-r--r--src/proj_api.h2
-rw-r--r--src/proj_constants.h3
-rw-r--r--src/proj_internal.h1
-rw-r--r--src/release.cpp2
-rw-r--r--src/strerrno.cpp1
-rw-r--r--src/transform.cpp16
-rw-r--r--src/transformations/helmert.cpp28
35 files changed, 1145 insertions, 1187 deletions
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 6b53363a..71393dab 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -195,62 +195,93 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
direction = opposite_direction(direction);
if( !P->alternativeCoordinateOperations.empty() ) {
- // Do a first pass and select the operations that match the area of use
- // and has the best accuracy.
- int iBest = -1;
- double bestAccuracy = std::numeric_limits<double>::max();
- int i = 0;
- for( const auto &alt: P->alternativeCoordinateOperations ) {
- bool spatialCriterionOK = false;
- if( direction == PJ_FWD ) {
- if( coord.xyzt.x >= alt.minxSrc &&
- coord.xyzt.y >= alt.minySrc &&
- coord.xyzt.x <= alt.maxxSrc &&
- coord.xyzt.y <= alt.maxySrc) {
- spatialCriterionOK = true;
+ constexpr int N_MAX_RETRY = 2;
+ int iExcluded[N_MAX_RETRY] = {-1, -1};
+
+ const int nOperations = static_cast<int>(
+ P->alternativeCoordinateOperations.size());
+
+ // We may need several attempts. For example the point at
+ // lon=-111.5 lat=45.26 falls into the bounding box of the Canadian
+ // ntv2_0.gsb grid, except that it is not in any of the subgrids, being
+ // in the US. We thus need another retry that will select the conus
+ // grid.
+ for( int iRetry = 0; iRetry <= N_MAX_RETRY; iRetry++ )
+ {
+ // Do a first pass and select the operations that match the area of use
+ // and has the best accuracy.
+ int iBest = -1;
+ double bestAccuracy = std::numeric_limits<double>::max();
+ for( int i = 0; i < nOperations; i++ ) {
+ if( i == iExcluded[0] || i == iExcluded[1] ) {
+ continue;
}
- } else {
- if( coord.xyzt.x >= alt.minxDst &&
- coord.xyzt.y >= alt.minyDst &&
- coord.xyzt.x <= alt.maxxDst &&
- coord.xyzt.y <= alt.maxyDst ) {
- spatialCriterionOK = true;
+ const auto &alt = P->alternativeCoordinateOperations[i];
+ bool spatialCriterionOK = false;
+ if( direction == PJ_FWD ) {
+ if( coord.xyzt.x >= alt.minxSrc &&
+ coord.xyzt.y >= alt.minySrc &&
+ coord.xyzt.x <= alt.maxxSrc &&
+ coord.xyzt.y <= alt.maxySrc) {
+ spatialCriterionOK = true;
+ }
+ } else {
+ if( coord.xyzt.x >= alt.minxDst &&
+ coord.xyzt.y >= alt.minyDst &&
+ coord.xyzt.x <= alt.maxxDst &&
+ coord.xyzt.y <= alt.maxyDst ) {
+ spatialCriterionOK = true;
+ }
}
- }
- if( spatialCriterionOK ) {
- // The offshore test is for the "Test bug 245 (use +datum=carthage)"
- // of testvarious. The long=10 lat=34 point belongs both to the
- // onshore and offshore Tunisia area of uses, but is slightly
- // onshore. So in a general way, prefer a onshore area to a
- // offshore one.
- if( iBest < 0 ||
- (alt.accuracy >= 0 && alt.accuracy < bestAccuracy &&
- !alt.isOffshore) ) {
- iBest = i;
- bestAccuracy = alt.accuracy;
+ if( spatialCriterionOK ) {
+ // The offshore test is for the "Test bug 245 (use +datum=carthage)"
+ // of testvarious. The long=10 lat=34 point belongs both to the
+ // onshore and offshore Tunisia area of uses, but is slightly
+ // onshore. So in a general way, prefer a onshore area to a
+ // offshore one.
+ if( iBest < 0 ||
+ (alt.accuracy >= 0 && alt.accuracy < bestAccuracy &&
+ !alt.isOffshore) ) {
+ iBest = i;
+ bestAccuracy = alt.accuracy;
+ }
}
}
- i ++;
- }
+ if( iBest < 0 ) {
+ break;
+ }
- if( iBest >= 0 ) {
const auto& alt = P->alternativeCoordinateOperations[iBest];
if( P->iCurCoordOp != iBest ) {
- std::string msg("Using coordinate operation ");
- msg += alt.name;
- pj_log(P->ctx, PJ_LOG_TRACE, msg.c_str());
+ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) {
+ std::string msg("Using coordinate operation ");
+ msg += alt.name;
+ pj_log(P->ctx, PJ_LOG_DEBUG, msg.c_str());
+ }
P->iCurCoordOp = iBest;
}
- return direction == PJ_FWD ?
+ PJ_COORD res = direction == PJ_FWD ?
pj_fwd4d( coord, alt.pj ) : pj_inv4d( coord, alt.pj );
+ if( proj_errno(alt.pj) == PJD_ERR_NETWORK_ERROR ) {
+ return proj_coord_error ();
+ }
+ if( res.xyzt.x != HUGE_VAL ) {
+ return res;
+ }
+ pj_log(P->ctx, PJ_LOG_DEBUG,
+ "Did not result in valid result. "
+ "Attempting a retry with another operation.");
+ if( iRetry == N_MAX_RETRY ) {
+ break;
+ }
+ iExcluded[iRetry] = iBest;
}
// In case we did not find an operation whose area of use is compatible
// with the input coordinate, then goes through again the list, and
// use the first operation that does not require grids.
- i = 0;
NS_PROJ::io::DatabaseContextPtr dbContext;
try
{
@@ -259,15 +290,18 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
}
}
catch( const std::exception& ) {}
- for( const auto &alt: P->alternativeCoordinateOperations ) {
+ for( int i = 0; i < nOperations; i++ ) {
+ const auto &alt = P->alternativeCoordinateOperations[i];
auto coordOperation = dynamic_cast<
NS_PROJ::operation::CoordinateOperation*>(alt.pj->iso_obj.get());
if( coordOperation ) {
if( coordOperation->gridsNeeded(dbContext, true).empty() ) {
if( P->iCurCoordOp != i ) {
- std::string msg("Using coordinate operation ");
- msg += alt.name;
- pj_log(P->ctx, PJ_LOG_TRACE, msg.c_str());
+ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) {
+ std::string msg("Using coordinate operation ");
+ msg += alt.name;
+ pj_log(P->ctx, PJ_LOG_DEBUG, msg.c_str());
+ }
P->iCurCoordOp = i;
}
if( direction == PJ_FWD ) {
@@ -278,7 +312,6 @@ similarly, but prefers the 2D resp. 3D interfaces if available.
}
}
}
- i++;
}
proj_errno_set (P, EINVAL);
diff --git a/src/Makefile.am b/src/Makefile.am
index 9b513fb3..8ab30a33 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,11 +7,11 @@ TESTS = geodtest
check_PROGRAMS = geodtest
AM_CPPFLAGS = -DPROJ_LIB=\"$(pkgdatadir)\" \
- -DMUTEX_@MUTEX_SETTING@ @JNI_INCLUDE@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ @TIFF_CFLAGS@ @TIFF_ENABLED_FLAGS@ @CURL_CFLAGS@ @CURL_ENABLED_FLAGS@
+ -DMUTEX_@MUTEX_SETTING@ -I$(top_srcdir)/include @SQLITE3_CFLAGS@ @TIFF_CFLAGS@ @TIFF_ENABLED_FLAGS@ @CURL_CFLAGS@ @CURL_ENABLED_FLAGS@
AM_CXXFLAGS = @CXX_WFLAGS@ @FLTO_FLAG@
include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesic.h \
- org_proj4_PJ.h proj_symbol_rename.h
+ proj_symbol_rename.h
EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \
bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \
@@ -42,7 +42,7 @@ geodtest_LDADD = libproj.la
lib_LTLIBRARIES = libproj.la
-libproj_la_LDFLAGS = -no-undefined -version-info 17:0:2
+libproj_la_LDFLAGS = -no-undefined -version-info 18:0:3
libproj_la_LIBADD = @SQLITE3_LIBS@ @TIFF_LIBS@ @CURL_LIBS@
libproj_la_SOURCES = \
@@ -198,7 +198,7 @@ libproj_la_SOURCES = \
\
datums.cpp datum_set.cpp transform.cpp \
geocent.cpp geocent.h utils.cpp \
- jniproj.cpp mutex.cpp initcache.cpp geodesic.c \
+ mutex.cpp initcache.cpp geodesic.c \
strtod.cpp \
\
4D_api.cpp pipeline.cpp \
diff --git a/src/apps/cct.cpp b/src/apps/cct.cpp
index d29b58fb..f7413872 100644
--- a/src/apps/cct.cpp
+++ b/src/apps/cct.cpp
@@ -161,19 +161,13 @@ static void logger(void *data, int level, const char *msg) {
/* if we use PJ_LOG_NONE we always want to print stuff to stream */
if (level == PJ_LOG_NONE) {
- fprintf(stream, "%s", msg);
+ fprintf(stream, "%s\n", msg);
return;
}
- /* should always print to stderr if level == PJ_LOG_ERROR */
- if (level == PJ_LOG_ERROR) {
- fprintf(stderr, "%s", msg);
- return;
- }
-
- /* otherwise only print if log level set by user is high enough */
- if (level <= log_tell)
- fprintf(stream, "%s", msg);
+ /* 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;
@@ -240,23 +234,25 @@ int main(int argc, char **argv) {
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 */
- proj_log_level (PJ_DEFAULT_CTX, static_cast<PJ_LOG_LEVEL>(verbose));
+ if( verbose > 0 ) {
+ proj_log_level (PJ_DEFAULT_CTX, static_cast<PJ_LOG_LEVEL>(verbose));
+ }
proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger);
if (opt_given (o, "version")) {
- print (PJ_LOG_NONE, "%s: %s\n", o->progname, pj_get_release ());
+ 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\n", o->progname, opt_arg (o, "output"));
+ 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\n", o->progname);
+ 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"));
@@ -287,7 +283,7 @@ int main(int argc, char **argv) {
/* 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'\n", o->progname, opt_arg (o, "c"));
+ print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'", o->progname, opt_arg (o, "c"));
free (o);
if (stdout != fout)
fclose (fout);
@@ -298,7 +294,7 @@ int main(int argc, char **argv) {
/* 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\n",
+ 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)
@@ -307,12 +303,12 @@ int main(int argc, char **argv) {
}
info = proj_pj_info (P);
- print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d\n", info.definition, argc, o->pargc);
+ 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\n");
+ print (PJ_LOG_ERROR, "Inverse operation not available");
if (stdout != fout)
fclose (fout);
return 1;
@@ -325,7 +321,7 @@ int main(int argc, char **argv) {
/* Allocate input buffer */
buf = static_cast<char*>(calloc (1, 10000));
if (nullptr==buf) {
- print (PJ_LOG_ERROR, "%s: Out of memory\n", o->progname);
+ print (PJ_LOG_ERROR, "%s: Out of memory", o->progname);
pj_free (P);
free (o);
if (stdout != fout)
@@ -341,7 +337,7 @@ int main(int argc, char **argv) {
char *c = column (buf, 1);
opt_eof_handler (o);
if (nullptr==ret) {
- print (PJ_LOG_ERROR, "Read error in record %d\n", (int) o->record_index);
+ 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);
@@ -359,7 +355,7 @@ int main(int argc, char **argv) {
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\n", o->progname, opt_filename (o), opt_record (o));
+ print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d", o->progname, opt_filename (o), opt_record (o));
continue;
}
@@ -395,7 +391,7 @@ int main(int argc, char **argv) {
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\n",
+ 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,
@@ -403,7 +399,7 @@ int main(int argc, char **argv) {
);
}
else
- print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s\n",
+ 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,
diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp
index 273233a7..6c85d4aa 100644
--- a/src/apps/cs2cs.cpp
+++ b/src/apps/cs2cs.cpp
@@ -430,19 +430,6 @@ int main(int argc, char **argv) {
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;
diff --git a/src/apps/gie.cpp b/src/apps/gie.cpp
index 6a67b55d..d9907776 100644
--- a/src/apps/gie.cpp
+++ b/src/apps/gie.cpp
@@ -1153,6 +1153,7 @@ static const struct errno_vs_err_const lookup[] = {
{"pjd_err_inconsistent_unit" , -59},
{"pjd_err_mutually_exclusive_args" , -60},
{"pjd_err_generic_error" , -61},
+ {"pjd_err_network_error" , -62},
{"pjd_err_dont_skip" , 5555},
{"pjd_err_unknown" , 9999},
{"pjd_err_enomem" , ENOMEM},
diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp
index 7fe08023..852cea04 100644
--- a/src/apps/proj.cpp
+++ b/src/apps/proj.cpp
@@ -385,17 +385,6 @@ int main(int argc, char **argv) {
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
emess(1,"invalid list option: l%c",arg[1]);
exit(0);
@@ -490,6 +479,14 @@ int main(int argc, char **argv) {
exit(0);
}
+ // Ugly hack. See https://github.com/OSGeo/PROJ/issues/1782
+ if( Proj->right == PJ_IO_UNITS_WHATEVER && Proj->descr &&
+ strncmp(Proj->descr, "General Oblique Transformation",
+ strlen("General Oblique Transformation")) == 0 )
+ {
+ Proj->right = PJ_IO_UNITS_PROJECTED;
+ }
+
if (inverse) {
if (!Proj->inv)
emess(3,"inverse projection not available");
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index f13e526b..27ea278a 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -78,7 +78,7 @@ struct OutputOptions {
static void usage() {
std::cerr
- << "usage: projinfo [-o formats] [-k crs|operation|ellipsoid] "
+ << "usage: projinfo [-o formats] [-k crs|operation|datum|ellipsoid] "
"[--summary] [-q]"
<< std::endl
<< " ([--area name_or_code] | "
@@ -185,6 +185,9 @@ static BaseObjectNNPtr buildObject(
} else if (kind == "ellipsoid" && tokens.size() == 2) {
auto urn = "urn:ogc:def:ellipsoid:" + tokens[0] + "::" + tokens[1];
obj = createFromUserInput(urn, dbContext).as_nullable();
+ } else if (kind == "datum" && tokens.size() == 2) {
+ auto urn = "urn:ogc:def:datum:" + 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] == '"' &&
@@ -206,6 +209,59 @@ static BaseObjectNNPtr buildObject(
}
}
}
+ } else if (dbContext && !kind.empty() && kind != "crs" &&
+ l_user_string.find(':') == std::string::npos) {
+ std::vector<AuthorityFactory::ObjectType> allowedTypes;
+ if (kind == "operation")
+ allowedTypes.push_back(
+ AuthorityFactory::ObjectType::COORDINATE_OPERATION);
+ else if (kind == "ellipsoid")
+ allowedTypes.push_back(
+ AuthorityFactory::ObjectType::ELLIPSOID);
+ else if (kind == "datum")
+ allowedTypes.push_back(AuthorityFactory::ObjectType::DATUM);
+ constexpr size_t limitResultCount = 10;
+ auto factory = AuthorityFactory::create(NN_NO_CHECK(dbContext),
+ std::string());
+ for (int pass = 0; pass <= 1; ++pass) {
+ const bool approximateMatch = (pass == 1);
+ auto res = factory->createObjectsFromName(
+ l_user_string, allowedTypes, approximateMatch,
+ limitResultCount);
+ if (res.size() == 1) {
+ obj = res.front().as_nullable();
+ } else {
+ for (const auto &l_obj : res) {
+ if (Identifier::isEquivalentName(
+ l_obj->nameStr().c_str(),
+ l_user_string.c_str())) {
+ obj = l_obj.as_nullable();
+ break;
+ }
+ }
+ if (obj) {
+ break;
+ }
+ }
+ if (res.size() > 1) {
+ std::string msg("several objects matching this name: ");
+ bool first = true;
+ for (const auto &l_obj : res) {
+ if (msg.size() > 200) {
+ msg += ", ...";
+ break;
+ }
+ if (!first) {
+ msg += ", ";
+ }
+ first = false;
+ msg += l_obj->nameStr();
+ }
+ std::cerr << context << ": " << msg << std::endl;
+ std::exit(1);
+ }
+ }
+
} else {
obj =
createFromUserInput(l_user_string, dbContext).as_nullable();
@@ -424,24 +480,13 @@ static void outputObject(
std::cout << "WKT1:GDAL string:" << std::endl;
}
- auto crs = nn_dynamic_pointer_cast<CRS>(obj);
- std::shared_ptr<IWKTExportable> objToExport;
- if (crs) {
- objToExport = nn_dynamic_pointer_cast<IWKTExportable>(
- crs->createBoundCRSToWGS84IfPossible(
- dbContext, allowUseIntermediateCRS));
- }
- if (!objToExport) {
- objToExport = wktExportable;
- }
-
auto formatter =
WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL);
if (outputOpt.singleLine) {
formatter->setMultiLine(false);
}
formatter->setStrict(outputOpt.strict);
- auto wkt = objToExport->exportToWKT(formatter.get());
+ auto wkt = wktExportable->exportToWKT(formatter.get());
if (outputOpt.c_ify) {
wkt = c_ify_string(wkt);
}
@@ -706,9 +751,7 @@ static void outputOperations(
}
first = false;
std::cout << "-------------------------------------" << std::endl;
- std::cout << "Operation n"
- "\xC2\xB0"
- << (i + 1) << ":" << std::endl
+ std::cout << "Operation No. " << (i + 1) << ":" << std::endl
<< std::endl;
outputOperationSummary(op, dbContext, gridAvailabilityUse);
std::cout << std::endl;
@@ -885,6 +928,8 @@ int main(int argc, char **argv) {
objectKind = "operation";
} else if (ci_equal(kind, "ellipsoid")) {
objectKind = "ellipsoid";
+ } else if (ci_equal(kind, "datum")) {
+ objectKind = "datum";
} else {
std::cerr << "Unrecognized value for option -k: " << kind
<< std::endl;
diff --git a/src/conversions/unitconvert.cpp b/src/conversions/unitconvert.cpp
index 377f384e..f8439aee 100644
--- a/src/conversions/unitconvert.cpp
+++ b/src/conversions/unitconvert.cpp
@@ -164,7 +164,8 @@ static double decimalyear_to_mjd(double decimalyear) {
double fractional_year;
double mjd;
- if( decimalyear < -10000 || decimalyear > 10000 )
+ // Written this way to deal with NaN input
+ if( !(decimalyear >= -10000 && decimalyear <= 10000) )
return 0;
year = lround(floor(decimalyear));
@@ -470,7 +471,7 @@ PJ *CONVERSION(unitconvert,0) {
const char* normalized_name = nullptr;
f = get_unit_conversion_factor(name, &xy_in_is_linear, &normalized_name);
if (f != 0.0) {
- proj_log_debug(P, "xy_in unit: %s", normalized_name);
+ proj_log_trace(P, "xy_in unit: %s", normalized_name);
} else {
f = pj_param (P->ctx, P->params, "dxy_in").f;
if (f == 0.0 || 1.0 / f == 0.0)
@@ -485,7 +486,7 @@ PJ *CONVERSION(unitconvert,0) {
const char* normalized_name = nullptr;
f = get_unit_conversion_factor(name, &xy_out_is_linear, &normalized_name);
if (f != 0.0) {
- proj_log_debug(P, "xy_out unit: %s", normalized_name);
+ proj_log_trace(P, "xy_out unit: %s", normalized_name);
} else {
f = pj_param (P->ctx, P->params, "dxy_out").f;
if (f == 0.0 || 1.0 / f == 0.0)
@@ -506,7 +507,7 @@ PJ *CONVERSION(unitconvert,0) {
const char* normalized_name = nullptr;
f = get_unit_conversion_factor(name, &z_in_is_linear, &normalized_name);
if (f != 0.0) {
- proj_log_debug(P, "z_in unit: %s", normalized_name);
+ proj_log_trace(P, "z_in unit: %s", normalized_name);
} else {
f = pj_param (P->ctx, P->params, "dz_in").f;
if (f == 0.0 || 1.0 / f == 0.0)
@@ -519,7 +520,7 @@ PJ *CONVERSION(unitconvert,0) {
const char* normalized_name = nullptr;
f = get_unit_conversion_factor(name, &z_out_is_linear, &normalized_name);
if (f != 0.0) {
- proj_log_debug(P, "z_out unit: %s", normalized_name);
+ proj_log_trace(P, "z_out unit: %s", normalized_name);
} else {
f = pj_param (P->ctx, P->params, "dz_out").f;
if (f == 0.0 || 1.0 / f == 0.0)
@@ -540,7 +541,7 @@ PJ *CONVERSION(unitconvert,0) {
if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */
Q->t_in_id = i;
- proj_log_debug(P, "t_in unit: %s", time_units[i].name);
+ proj_log_trace(P, "t_in unit: %s", time_units[i].name);
}
s = nullptr;
@@ -550,7 +551,7 @@ PJ *CONVERSION(unitconvert,0) {
if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */
Q->t_out_id = i;
- proj_log_debug(P, "t_out unit: %s", time_units[i].name);
+ proj_log_trace(P, "t_out unit: %s", time_units[i].name);
}
return P;
diff --git a/src/ell_set.cpp b/src/ell_set.cpp
index bb46b3a4..899ec1bf 100644
--- a/src/ell_set.cpp
+++ b/src/ell_set.cpp
@@ -116,9 +116,9 @@ int pj_ellipsoid (PJ *P) {
if (0 != ellps_spherification (P))
return 4;
- proj_log_debug (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d",
+ proj_log_trace (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d",
P->a, P->f!=0? 1/P->f: 0, proj_errno (P));
- proj_log_debug (P, "pj_ellipsoid - final: %s %s %s %s",
+ proj_log_trace (P, "pj_ellipsoid - final: %s %s %s %s",
P->def_size? P->def_size: empty,
P->def_shape? P->def_shape: empty,
P->def_spherification? P->def_spherification: empty,
diff --git a/src/filemanager.hpp b/src/filemanager.hpp
index b84753ce..554bd325 100644
--- a/src/filemanager.hpp
+++ b/src/filemanager.hpp
@@ -90,7 +90,8 @@ class File {
// ---------------------------------------------------------------------------
-std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT* ctx, const char* filename);
+std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT *ctx,
+ const char *filename);
NS_PROJ_END
diff --git a/src/geodesic.h b/src/geodesic.h
index 5d230531..e2265c89 100644
--- a/src/geodesic.h
+++ b/src/geodesic.h
@@ -158,7 +158,7 @@
GEODESIC_VERSION_PATCH)
#if !defined(GEOD_DLL)
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) && defined(PROJ_MSVC_DLL_EXPORT)
#define GEOD_DLL __declspec(dllexport)
#elif defined(__GNUC__)
#define GEOD_DLL __attribute__ ((visibility("default")))
diff --git a/src/grids.cpp b/src/grids.cpp
index d5f961f7..24fcfe83 100644
--- a/src/grids.cpp
+++ b/src/grids.cpp
@@ -1345,7 +1345,6 @@ VerticalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
auto fp = FileManager::open_resource_file(ctx, filename.c_str());
if (!fp) {
- ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
const auto actualName(fp->name());
@@ -2275,7 +2274,6 @@ HorizontalShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
auto fp = FileManager::open_resource_file(ctx, filename.c_str());
if (!fp) {
- ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
const auto actualName(fp->name());
@@ -2655,7 +2653,6 @@ GenericShiftGridSet::open(PJ_CONTEXT *ctx, const std::string &filename) {
auto fp = FileManager::open_resource_file(ctx, filename.c_str());
if (!fp) {
- ctx->last_errno = 0; /* don't treat as a persistent error */
return nullptr;
}
const auto actualName(fp->name());
@@ -2762,9 +2759,12 @@ ListOfGenericGrids pj_generic_grid_init(PJ *P, const char *gridkey) {
auto gridSet = GenericShiftGridSet::open(P->ctx, gridname);
if (!gridSet) {
if (!canFail) {
- pj_ctx_set_errno(P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ if (proj_context_errno(P->ctx) != PJD_ERR_NETWORK_ERROR) {
+ pj_ctx_set_errno(P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ }
return {};
}
+ pj_ctx_set_errno(P->ctx, 0); // don't treat as a persistent error
} else {
grids.emplace_back(std::move(gridSet));
}
@@ -2803,9 +2803,12 @@ static ListOfHGrids getListOfGridSets(PJ_CONTEXT *ctx, const char *grids) {
auto gridSet = HorizontalShiftGridSet::open(ctx, gridname);
if (!gridSet) {
if (!canFail) {
- pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ if (proj_context_errno(ctx) != PJD_ERR_NETWORK_ERROR) {
+ pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ }
return {};
}
+ pj_ctx_set_errno(ctx, 0); // don't treat as a persistent error
} else {
list.emplace_back(std::move(gridSet));
}
@@ -3228,9 +3231,12 @@ ListOfVGrids pj_vgrid_init(PJ *P, const char *gridkey) {
auto gridSet = VerticalShiftGridSet::open(P->ctx, gridname);
if (!gridSet) {
if (!canFail) {
- pj_ctx_set_errno(P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ if (proj_context_errno(P->ctx) != PJD_ERR_NETWORK_ERROR) {
+ pj_ctx_set_errno(P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID);
+ }
return {};
}
+ pj_ctx_set_errno(P->ctx, 0); // don't treat as a persistent error
} else {
grids.emplace_back(std::move(gridSet));
}
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 5df4c513..4c98af3d 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -1146,15 +1146,9 @@ PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) {
// ---------------------------------------------------------------------------
-/** \brief Return whether two objects are equivalent.
- *
- * @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) {
+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) {
@@ -1176,7 +1170,50 @@ int proj_is_equivalent_to(const PJ *obj, const PJ *other,
return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
})(criterion);
- return obj->iso_obj->isEquivalentTo(other->iso_obj.get(), cppCriterion);
+ 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);
}
// ---------------------------------------------------------------------------
@@ -2159,6 +2196,10 @@ PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) {
* The candidate CRSs are either hard-coded, or looked in the database when
* it is available.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* 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.
@@ -2727,16 +2768,19 @@ static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
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;
+ } else if (refDatum->identifiers().size() == 1) {
+ const auto &id = refDatum->identifiers()[0];
+ const auto aliases =
+ authFactory->databaseContext()->getAliases(
+ *id->codeSpace(), id->code(), refDatum->nameStr(),
+ "geodetic_datum", std::string());
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(
+ datumName.c_str(), alias.c_str())) {
+ datumName = refDatum->nameStr();
+ break;
+ }
+ }
}
}
}
@@ -7783,6 +7827,34 @@ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
// ---------------------------------------------------------------------------
+/** \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<const CoordinateOperation *>(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.
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
index 97900bda..f2e4de4c 100644
--- a/src/iso19111/common.cpp
+++ b/src/iso19111/common.cpp
@@ -901,19 +901,19 @@ void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const {
// ---------------------------------------------------------------------------
bool IdentifiedObject::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other);
if (!otherIdObj)
return false;
- return _isEquivalentTo(otherIdObj, criterion);
+ return _isEquivalentTo(otherIdObj, criterion, dbContext);
}
// ---------------------------------------------------------------------------
-bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj,
- util::IComparable::Criterion criterion)
- PROJ_PURE_DEFN {
+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;
@@ -922,12 +922,19 @@ bool IdentifiedObject::_isEquivalentTo(const IdentifiedObject *otherIdObj,
} else {
if (!metadata::Identifier::isEquivalentName(
nameStr().c_str(), otherIdObj->nameStr().c_str())) {
- return false;
+ return hasEquivalentNameToUsingAlias(otherIdObj, dbContext);
}
}
return true;
}
+// ---------------------------------------------------------------------------
+
+bool IdentifiedObject::hasEquivalentNameToUsingAlias(
+ const IdentifiedObject *, const io::DatabaseContextPtr &) const {
+ return false;
+}
+
//! @endcond
// ---------------------------------------------------------------------------
@@ -1092,8 +1099,8 @@ void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const {
//! @cond Doxygen_Suppress
bool ObjectDomain::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDomain = dynamic_cast<const ObjectDomain *>(other);
if (!otherDomain)
return false;
@@ -1106,7 +1113,7 @@ bool ObjectDomain::_isEquivalentTo(
return false;
return domainOfValidity().get() == nullptr ||
domainOfValidity()->_isEquivalentTo(
- otherDomain->domainOfValidity().get(), criterion);
+ otherDomain->domainOfValidity().get(), criterion, dbContext);
}
//! @endcond
@@ -1249,14 +1256,14 @@ void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const {
//! @cond Doxygen_Suppress
bool ObjectUsage::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other);
if (!otherObjUsage)
return false;
// TODO: incomplete
- return IdentifiedObject::_isEquivalentTo(other, criterion);
+ return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 7c0515c7..a1650046 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -1070,11 +1070,11 @@ void OperationMethod::_exportToJSON(
//! @cond Doxygen_Suppress
bool OperationMethod::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOM = dynamic_cast<const OperationMethod *>(other);
if (otherOM == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
// TODO test formula and formulaCitation
@@ -1086,7 +1086,8 @@ bool OperationMethod::_isEquivalentTo(
}
if (criterion == util::IComparable::Criterion::STRICT) {
for (size_t i = 0; i < paramsSize; i++) {
- if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion)) {
+ if (!params[i]->_isEquivalentTo(otherParams[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -1096,8 +1097,8 @@ bool OperationMethod::_isEquivalentTo(
bool found = false;
for (size_t j = 0; j < paramsSize; j++) {
if (candidateIndices[j] &&
- params[i]->_isEquivalentTo(otherParams[j].get(),
- criterion)) {
+ params[i]->_isEquivalentTo(otherParams[j].get(), criterion,
+ dbContext)) {
candidateIndices[j] = false;
found = true;
break;
@@ -1343,14 +1344,14 @@ bool OperationParameterValue::convertFromAbridged(
//! @cond Doxygen_Suppress
bool OperationParameterValue::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOPV = dynamic_cast<const OperationParameterValue *>(other);
if (otherOPV == nullptr) {
return false;
}
- if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(),
- criterion)) {
+ if (!d->parameter->_isEquivalentTo(otherOPV->d->parameter.get(), criterion,
+ dbContext)) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
@@ -1358,7 +1359,7 @@ bool OperationParameterValue::_isEquivalentTo(
otherOPV->d->parameterValue.get(), criterion);
}
if (d->parameterValue->_isEquivalentTo(otherOPV->d->parameterValue.get(),
- criterion)) {
+ criterion, dbContext)) {
return true;
}
if (d->parameter->getEPSGCode() ==
@@ -1448,16 +1449,16 @@ OperationParameter::create(const util::PropertyMap &properties) {
//! @cond Doxygen_Suppress
bool OperationParameter::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherOP = dynamic_cast<const OperationParameter *>(other);
if (otherOP == nullptr) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
- return IdentifiedObject::_isEquivalentTo(other, criterion);
+ return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
}
- if (IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ if (IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return true;
}
auto l_epsgCode = getEPSGCode();
@@ -1761,19 +1762,20 @@ static SingleOperationNNPtr createPROJBased(
//! @cond Doxygen_Suppress
bool SingleOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
- return _isEquivalentTo(other, criterion, false);
+ 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<const SingleOperation *>(other);
if (otherSO == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
@@ -1783,7 +1785,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
bool equivalentMethods =
(criterion == util::IComparable::Criterion::EQUIVALENT &&
methodEPSGCode != 0 && methodEPSGCode == otherMethodEPSGCode) ||
- d->method_->_isEquivalentTo(otherSO->d->method_.get(), criterion);
+ 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 &&
@@ -1856,7 +1859,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
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);
+ return otherSO->_isEquivalentTo(this, criterion, dbContext);
} else if ((methodEPSGCode == EPSG_CODE_METHOD_MERCATOR_VARIANT_A &&
otherMethodEPSGCode ==
EPSG_CODE_METHOD_MERCATOR_VARIANT_B) ||
@@ -1872,7 +1875,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
auto eqConv =
conv->convertToOtherMethod(otherMethodEPSGCode);
if (eqConv) {
- return eqConv->_isEquivalentTo(other, criterion);
+ return eqConv->_isEquivalentTo(other, criterion,
+ dbContext);
}
}
}
@@ -1890,7 +1894,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
return false;
}
for (size_t i = 0; i < valuesSize; i++) {
- if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion)) {
+ if (!values[i]->_isEquivalentTo(otherValues[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -1911,7 +1916,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
bool sameNameDifferentValue = false;
for (size_t j = 0; j < otherValuesSize; j++) {
if (candidateIndices[j] &&
- values[i]->_isEquivalentTo(otherValues[j].get(), criterion)) {
+ values[i]->_isEquivalentTo(otherValues[j].get(), criterion,
+ dbContext)) {
candidateIndices[j] = false;
equivalent = true;
break;
@@ -1923,7 +1929,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
return false;
sameNameDifferentValue =
opParamvalue->parameter()->_isEquivalentTo(
- otherOpParamvalue->parameter().get(), criterion);
+ otherOpParamvalue->parameter().get(), criterion,
+ dbContext);
if (sameNameDifferentValue) {
candidateIndices[j] = false;
break;
@@ -1951,13 +1958,13 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
->parameterValue(
EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL)
.get(),
- criterion) &&
+ criterion, dbContext) &&
value_2nd->_isEquivalentTo(
otherSO
->parameterValue(
EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL)
.get(),
- criterion);
+ criterion, dbContext);
}
}
}
@@ -1989,7 +1996,7 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
// In the case the arguments don't perfectly match, try the reverse
// check.
if (equivalent && foundMissingArgs && !inOtherDirection) {
- return otherSO->_isEquivalentTo(this, criterion, true);
+ return otherSO->_isEquivalentTo(this, criterion, dbContext, true);
}
// Equivalent formulations of 2SP can have different parameters
@@ -2004,8 +2011,8 @@ bool SingleOperation::_isEquivalentTo(const util::IComparable *other,
auto otherAs1SP = otherConv->convertToOtherMethod(
EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
if (thisAs1SP && otherAs1SP) {
- equivalent =
- thisAs1SP->_isEquivalentTo(otherAs1SP.get(), criterion);
+ equivalent = thisAs1SP->_isEquivalentTo(otherAs1SP.get(),
+ criterion, dbContext);
}
}
}
@@ -2415,9 +2422,9 @@ void ParameterValue::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
//! @cond Doxygen_Suppress
-bool ParameterValue::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+bool ParameterValue::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &) const {
auto otherPV = dynamic_cast<const ParameterValue *>(other);
if (otherPV == nullptr) {
return false;
@@ -8763,7 +8770,8 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames(
}
}
- if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON) {
+ if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON ||
+ methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) {
auto fileParameter =
parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE,
EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE);
@@ -9437,6 +9445,19 @@ void Transformation::_exportToPROJString(
}
}
+ if (methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD) {
+ 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,
@@ -10189,10 +10210,12 @@ void ConcatenatedOperation::_exportToPROJString(
//! @cond Doxygen_Suppress
bool ConcatenatedOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCO = dynamic_cast<const ConcatenatedOperation *>(other);
- if (otherCO == nullptr || !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ if (otherCO == nullptr ||
+ (criterion == util::IComparable::Criterion::STRICT &&
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &steps = operations();
@@ -10201,7 +10224,8 @@ bool ConcatenatedOperation::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < steps.size(); i++) {
- if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion)) {
+ if (!steps[i]->_isEquivalentTo(otherSteps[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -11313,9 +11337,17 @@ struct FilterResults {
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) {
+ if (!lastHasGrids && sameNameOrEmptyName) {
continue;
}
// If we had only operations involving grids, but one
@@ -12613,6 +12645,18 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
// 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) {
@@ -12666,7 +12710,7 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot(
#endif
createTransformations(candidateSrcGeod, candidateDstGeod,
opsFirst[0], isNullFirst);
- if (!res.empty() && hasResultSetOnlyResultsWithPROJStep(res)) {
+ if (!res.empty() && !hasResultSetOnlyResultsWithPROJStep(res)) {
return;
}
}
@@ -14870,14 +14914,15 @@ void InverseCoordinateOperation::_exportToPROJString(
// ---------------------------------------------------------------------------
bool InverseCoordinateOperation::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherICO = dynamic_cast<const InverseCoordinateOperation *>(other);
if (otherICO == nullptr ||
- !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
- return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion);
+ return inverse()->_isEquivalentTo(otherICO->inverse().get(), criterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp
index 6769b486..fc4b7492 100644
--- a/src/iso19111/coordinatesystem.cpp
+++ b/src/iso19111/coordinatesystem.cpp
@@ -429,8 +429,8 @@ void CoordinateSystemAxis::_exportToJSON(
//! @cond Doxygen_Suppress
bool CoordinateSystemAxis::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCSA = dynamic_cast<const CoordinateSystemAxis *>(other);
if (otherCSA == nullptr) {
return false;
@@ -441,7 +441,7 @@ bool CoordinateSystemAxis::_isEquivalentTo(
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
- if (!IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if (abbreviation() != otherCSA->abbreviation()) {
@@ -581,11 +581,13 @@ void CoordinateSystem::_exportToJSON(
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);
+ {
+ auto axisContext(writer.MakeArrayContext(false));
+ const auto &l_axisList = axisList();
+ for (auto &axis : l_axisList) {
+ formatter->setOmitTypeInImmediateChild();
+ axis->_exportToJSON(formatter);
+ }
}
if (formatter->outputId()) {
@@ -599,11 +601,11 @@ void CoordinateSystem::_exportToJSON(
//! @cond Doxygen_Suppress
bool CoordinateSystem::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCS = dynamic_cast<const CoordinateSystem *>(other);
if (otherCS == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
const auto &list = axisList();
@@ -615,7 +617,8 @@ bool CoordinateSystem::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < list.size(); i++) {
- if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion)) {
+ if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion,
+ dbContext)) {
return false;
}
}
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 49cc050f..d71d527e 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -49,6 +49,7 @@
#include <algorithm>
#include <cassert>
+#include <cmath>
#include <cstring>
#include <iostream>
#include <memory>
@@ -390,7 +391,7 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
if (boundCRS) {
if (boundCRS->hubCRS()->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return NN_NO_CHECK(boundCRS);
}
}
@@ -399,16 +400,16 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible(
auto geogCRS = extractGeographicCRS();
auto hubCRS = util::nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326);
if (geodCRS && !geogCRS) {
- if (geodCRS->_isEquivalentTo(
- GeographicCRS::EPSG_4978.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (geodCRS->_isEquivalentTo(GeographicCRS::EPSG_4978.get(),
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
return thisAsCRS;
}
hubCRS = util::nn_static_pointer_cast<CRS>(GeodeticCRS::EPSG_4978);
} else if (!geogCRS ||
geogCRS->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
return thisAsCRS;
} else {
geodCRS = geogCRS;
@@ -595,12 +596,44 @@ CRSNNPtr CRS::alterId(const std::string &authName,
//! @cond Doxygen_Suppress
-static bool isAxisListNorthEast(
+static bool mustAxisOrderBeSwitchedForVisualizationInternal(
const std::vector<cs::CoordinateSystemAxisNNPtr> &axisList) {
const auto &dir0 = axisList[0]->direction();
const auto &dir1 = axisList[1]->direction();
- return (&dir0 == &cs::AxisDirection::NORTH &&
- &dir1 == &cs::AxisDirection::EAST);
+ 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;
}
// ---------------------------------------------------------------------------
@@ -616,12 +649,14 @@ bool CRS::mustAxisOrderBeSwitchedForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
- return isAxisListNorthEast(geogCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ geogCRS->coordinateSystem()->axisList());
}
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
- return isAxisListNorthEast(projCRS->coordinateSystem()->axisList());
+ return mustAxisOrderBeSwitchedForVisualizationInternal(
+ projCRS->coordinateSystem()->axisList());
}
return false;
@@ -655,7 +690,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs = axisList.size() == 2
? cs::EllipsoidalCS::create(util::PropertyMap(),
axisList[1], axisList[0])
@@ -670,7 +705,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
if (projCRS) {
const auto &axisList = projCRS->coordinateSystem()->axisList();
- if (isAxisListNorthEast(axisList)) {
+ if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
auto cs =
axisList.size() == 2
? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
@@ -695,6 +730,10 @@ CRSNNPtr CRS::normalizeForVisualization() const {
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* 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.
@@ -987,18 +1026,19 @@ const cs::CoordinateSystemNNPtr &SingleCRS::coordinateSystem() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
bool SingleCRS::baseIsEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherSingleCRS = dynamic_cast<const SingleCRS *>(other);
if (otherSingleCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !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)) {
+ if (!thisDatum->_isEquivalentTo(otherDatum.get(), criterion,
+ dbContext)) {
return false;
}
} else {
@@ -1009,7 +1049,8 @@ bool SingleCRS::baseIsEquivalentTo(
// TODO test DatumEnsemble
return d->coordinateSystem->_isEquivalentTo(
- otherSingleCRS->d->coordinateSystem.get(), criterion) &&
+ otherSingleCRS->d->coordinateSystem.get(), criterion,
+ dbContext) &&
getExtensionProj4() == otherSingleCRS->getExtensionProj4();
}
@@ -1544,13 +1585,13 @@ getStandardCriterion(util::IComparable::Criterion criterion) {
//! @cond Doxygen_Suppress
bool GeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
const auto standardCriterion = getStandardCriterion(criterion);
auto otherGeodCRS = dynamic_cast<const GeodeticCRS *>(other);
// TODO test velocityModel
return otherGeodCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, standardCriterion);
+ SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext);
}
//! @endcond
@@ -1611,6 +1652,10 @@ static bool hasCodeCompatibleOfAuthorityFactory(
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* The method returns a list of matching reference CRS, and the percentage
* (0-100) of confidence in the match:
* <ul>
@@ -1651,6 +1696,9 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
std::list<Pair> 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
@@ -1664,7 +1712,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
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);
+ const bool isEq = _isEquivalentTo(crs.get(), crsCriterion, dbContext);
if (nameEquivalent && isEq && (!authorityFactory || nameEqual)) {
res.emplace_back(util::nn_static_pointer_cast<GeodeticCRS>(crs),
nameEqual ? 100 : 90);
@@ -1699,13 +1747,14 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisDatum(datum());
auto searchByDatum = [this, &authorityFactory, &res, &thisDatum,
- &geodetic_crs_type, crsCriterion]() {
+ &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)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion,
+ dbContext)) {
res.emplace_back(crs, 70);
}
}
@@ -1717,7 +1766,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisEllipsoid(ellipsoid());
auto searchByEllipsoid = [this, &authorityFactory, &res, &thisDatum,
&thisEllipsoid, &geodetic_crs_type,
- l_implicitCS]() {
+ l_implicitCS, &dbContext]() {
const auto ellipsoids =
thisEllipsoid->identifiers().empty()
? authorityFactory->createEllipsoidFromExisting(
@@ -1735,15 +1784,17 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (crsDatum &&
crsDatum->ellipsoid()->_isEquivalentTo(
ellps.get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
crsDatum->primeMeridian()->_isEquivalentTo(
thisDatum->primeMeridian().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
(!l_implicitCS ||
coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::
- EQUIVALENT))) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext))) {
res.emplace_back(crs, 60);
}
}
@@ -1776,7 +1827,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
authorityFactory->databaseContext(),
*id->codeSpace())
->createGeodeticCRS(id->code());
- bool match = _isEquivalentTo(crs.get(), crsCriterion);
+ bool match =
+ _isEquivalentTo(crs.get(), crsCriterion, dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -1794,7 +1846,7 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
auto crs = util::nn_dynamic_pointer_cast<GeodeticCRS>(obj);
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
- if (_isEquivalentTo(crs.get(), crsCriterion)) {
+ if (_isEquivalentTo(crs.get(), crsCriterion, dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -1824,8 +1876,8 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisCS(coordinateSystem());
// Sort results
- res.sort([&thisName, &thisDatum, &thisCS](const Pair &a,
- const Pair &b) {
+ res.sort([&thisName, &thisDatum, &thisCS, &dbContext](const Pair &a,
+ const Pair &b) {
// First consider confidence
if (a.second > b.second) {
return true;
@@ -1849,9 +1901,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &bDatum(b.first->datum());
if (thisDatum && aDatum && bDatum) {
const auto thisEquivADatum(thisDatum->_isEquivalentTo(
- aDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ aDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBDatum(thisDatum->_isEquivalentTo(
- bDatum.get(), util::IComparable::Criterion::EQUIVALENT));
+ bDatum.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivADatum && !thisEquivBDatum) {
return true;
@@ -1865,9 +1919,11 @@ GeodeticCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &aCS(a.first->coordinateSystem());
const auto &bCS(b.first->coordinateSystem());
const auto thisEquivACs(thisCS->_isEquivalentTo(
- aCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ aCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
const auto thisEquivBCs(thisCS->_isEquivalentTo(
- bCS.get(), util::IComparable::Criterion::EQUIVALENT));
+ bCS.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext));
if (thisEquivACs && !thisEquivBCs) {
return true;
}
@@ -2084,14 +2140,14 @@ bool GeographicCRS::is2DPartOf3D(util::nn<const GeographicCRS *> other)
//! @cond Doxygen_Suppress
bool GeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherGeogCRS = dynamic_cast<const GeographicCRS *>(other);
if (otherGeogCRS == nullptr) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
- if (GeodeticCRS::_isEquivalentTo(other, standardCriterion)) {
+ if (GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext)) {
return true;
}
if (criterion !=
@@ -2110,7 +2166,7 @@ bool GeographicCRS::_isEquivalentTo(
cs::EllipsoidalCS::AxisOrder::LONG_EAST_LAT_NORTH
? cs::EllipsoidalCS::createLatitudeLongitude(unit)
: cs::EllipsoidalCS::createLongitudeLatitude(unit))
- ->GeodeticCRS::_isEquivalentTo(other, standardCriterion);
+ ->GeodeticCRS::_isEquivalentTo(other, standardCriterion, dbContext);
}
return false;
}
@@ -2682,12 +2738,12 @@ VerticalCRS::create(const util::PropertyMap &properties,
//! @cond Doxygen_Suppress
bool VerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherVertCRS = dynamic_cast<const VerticalCRS *>(other);
// TODO test geoidModel and velocityModel
return otherVertCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
//! @endcond
@@ -2698,6 +2754,10 @@ bool VerticalCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* 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
@@ -2722,6 +2782,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -2733,12 +2795,11 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createVerticalCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -2756,8 +2817,8 @@ VerticalCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
assert(crs);
auto crsNN = NN_NO_CHECK(crs);
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -2922,19 +2983,20 @@ DerivedCRS::derivingConversionRef() PROJ_PURE_DEFN {
// ---------------------------------------------------------------------------
-bool DerivedCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+bool DerivedCRS::_isEquivalentTo(
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRS *>(other);
const auto standardCriterion = getStandardCriterion(criterion);
if (otherDerivedCRS == nullptr ||
- !SingleCRS::baseIsEquivalentTo(other, standardCriterion)) {
+ !SingleCRS::baseIsEquivalentTo(other, standardCriterion, dbContext)) {
return false;
}
return d->baseCRS_->_isEquivalentTo(otherDerivedCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->derivingConversion_->_isEquivalentTo(
- otherDerivedCRS->d->derivingConversion_.get(),
- standardCriterion);
+ otherDerivedCRS->d->derivingConversion_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -3367,11 +3429,11 @@ ProjectedCRS::create(const util::PropertyMap &properties,
// ---------------------------------------------------------------------------
bool ProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherProjCRS = dynamic_cast<const ProjectedCRS *>(other);
return otherProjCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -3394,23 +3456,33 @@ 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)) {
+ 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 (!formatter->omitZUnitConversion())
+ if (zUnit)
formatter->addParam("z_in", "m");
+
if (projUnit.empty()) {
formatter->addParam("xy_out", toSI);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_out", toSI);
} else {
formatter->addParam("xy_out", projUnit);
- if (!formatter->omitZUnitConversion())
- formatter->addParam("z_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()) {
@@ -3479,6 +3551,10 @@ void ProjectedCRS::addUnitConvertAndAxisSwap(io::PROJStringFormatter *formatter,
* The candidate CRSs are either hard-coded, or looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* 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.
@@ -3545,14 +3621,19 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
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())) {
+ .get(),
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
if (baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("WGS 84 / UTM zone ", zone, north));
res.emplace_back(
@@ -3566,7 +3647,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4267.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD27 / UTM zone ", zone, north));
res.emplace_back(
@@ -3581,7 +3662,7 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
north &&
baseRes.front().first->_isEquivalentTo(
GeographicCRS::EPSG_4269.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT, dbContext)) {
std::string crsName(
computeUTMCRSName("NAD83 / UTM zone ", zone, north));
res.emplace_back(
@@ -3613,9 +3694,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
*id->codeSpace())
->createProjectedCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -3636,9 +3717,9 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ crs.get(), util::IComparable::Criterion::
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3650,10 +3731,12 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
l_baseCRS->_isEquivalentTo(
crs->baseCRS().get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS) &&
+ EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
objects.size() == 1) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -3720,17 +3803,21 @@ ProjectedCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (_isEquivalentTo(crs.get(),
util::IComparable::Criterion::
- EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)) {
+ 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) &&
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext) &&
derivingConversionRef()->_isEquivalentTo(
crs->derivingConversionRef().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (coordinateSystem()->_isEquivalentTo(
crs->coordinateSystem().get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, 70);
} else {
res.emplace_back(crs, 50);
@@ -3960,12 +4047,12 @@ void CompoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool CompoundCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherCompoundCRS = dynamic_cast<const CompoundCRS *>(other);
if (otherCompoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto &components = componentReferenceSystems();
@@ -3974,8 +4061,8 @@ bool CompoundCRS::_isEquivalentTo(
return false;
}
for (size_t i = 0; i < components.size(); i++) {
- if (!components[i]->_isEquivalentTo(otherComponents[i].get(),
- criterion)) {
+ if (!components[i]->_isEquivalentTo(otherComponents[i].get(), criterion,
+ dbContext)) {
return false;
}
}
@@ -3989,6 +4076,10 @@ bool CompoundCRS::_isEquivalentTo(
* The candidate CRSs are looked in the database when
* authorityFactory is not null.
*
+ * Note that the implementation uses a set of heuristics to have a good
+ * compromise of successful identifications over execution time. It might miss
+ * legitimate matches in some circumstances.
+ *
* 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.
@@ -4015,6 +4106,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
const auto &thisName(nameStr());
if (authorityFactory) {
+ const io::DatabaseContextNNPtr &dbContext =
+ authorityFactory->databaseContext();
const bool unsignificantName = thisName.empty() ||
ci_equal(thisName, "unknown") ||
@@ -4028,12 +4121,11 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
if (hasCodeCompatibleOfAuthorityFactory(id, authorityFactory)) {
try {
auto crs = io::AuthorityFactory::create(
- authorityFactory->databaseContext(),
- *id->codeSpace())
+ dbContext, *id->codeSpace())
->createCompoundCRS(id->code());
bool match = _isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT);
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext);
res.emplace_back(crs, match ? 100 : 25);
return res;
} catch (const std::exception &) {
@@ -4054,8 +4146,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
thisName.c_str(), crs->nameStr().c_str());
foundEquivalentName |= eqName;
if (_isEquivalentTo(
- crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ crs.get(), util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
if (crs->nameStr() == thisName) {
res.clear();
res.emplace_back(crsNN, 100);
@@ -4122,7 +4214,8 @@ CompoundCRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
if (_isEquivalentTo(crs.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ util::IComparable::Criterion::EQUIVALENT,
+ dbContext)) {
res.emplace_back(crs, unsignificantName ? 90 : 70);
} else {
res.emplace_back(crs, 25);
@@ -4494,20 +4587,22 @@ void BoundCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool BoundCRS::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherBoundCRS = dynamic_cast<const BoundCRS *>(other);
if (otherBoundCRS == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !ObjectUsage::_isEquivalentTo(other, criterion))) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
const auto standardCriterion = getStandardCriterion(criterion);
return d->baseCRS_->_isEquivalentTo(otherBoundCRS->d->baseCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->hubCRS_->_isEquivalentTo(otherBoundCRS->d->hubCRS_.get(),
- criterion) &&
+ criterion, dbContext) &&
d->transformation_->_isEquivalentTo(
- otherBoundCRS->d->transformation_.get(), standardCriterion);
+ otherBoundCRS->d->transformation_.get(), standardCriterion,
+ dbContext);
}
// ---------------------------------------------------------------------------
@@ -4518,10 +4613,36 @@ std::list<std::pair<CRSNNPtr, int>>
BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
typedef std::pair<CRSNNPtr, int> Pair;
std::list<Pair> res;
- if (authorityFactory &&
- d->hubCRS_->_isEquivalentTo(GeographicCRS::EPSG_4326.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
+ if (!authorityFactory)
+ return res;
+ std::list<Pair> 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<double>{0, 0, 0, 0, 0, 0, 0}) {
+ refIsNullTransform = true;
+ }
+ }
+
for (const auto &pair : resTemp) {
const auto &candidateBaseCRS = pair.first;
auto projCRS =
@@ -4532,54 +4653,47 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
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);
- std::string refTransfPROJString;
- bool refTransfPROJStringValid = false;
- try {
- refTransfPROJString =
- d->transformation_->exportToPROJString(
- io::PROJStringFormatter::create().get());
- refTransfPROJStringValid = true;
- if (refTransfPROJString == "+proj=axisswap +order=2,1") {
- refTransfPROJString.clear();
- }
- } catch (const std::exception &) {
- }
+
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 (isTOWGS84Compatible()) {
- auto params =
- transformation()->getTOWGS84Parameters();
- if (params ==
- std::vector<double>{0, 0, 0, 0, 0, 0, 0}) {
- res.emplace_back(create(candidateBaseCRS,
- d->hubCRS_,
- transformation()),
- pair.second);
- foundOp = true;
- break;
- }
+ if (refIsNullTransform) {
+ res.emplace_back(create(candidateBaseCRS,
+ d->hubCRS_,
+ transformation()),
+ pair.second);
+ foundOp = true;
+ break;
}
continue;
}
try {
- opTransfPROJString = op->exportToPROJString(
+ 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) ||
- op->_isEquivalentTo(
- d->transformation_.get(),
- util::IComparable::Criterion::EQUIVALENT)) {
- res.emplace_back(
+ 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))),
@@ -4596,7 +4710,7 @@ BoundCRS::_identify(const io::AuthorityFactoryPtr &authorityFactory) const {
}
}
}
- return res;
+ return !resMatchOfTransfToWGS84.empty() ? resMatchOfTransfToWGS84 : res;
}
// ---------------------------------------------------------------------------
@@ -4758,11 +4872,11 @@ void DerivedGeodeticCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeodeticCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeodeticCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -4908,11 +5022,11 @@ void DerivedGeographicCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedGeographicCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedGeographicCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5055,11 +5169,11 @@ void DerivedProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
bool DerivedProjectedCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedProjectedCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5185,11 +5299,11 @@ void TemporalCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool TemporalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTemporalCRS = dynamic_cast<const TemporalCRS *>(other);
return otherTemporalCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5323,11 +5437,11 @@ void EngineeringCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool EngineeringCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherEngineeringCRS = dynamic_cast<const EngineeringCRS *>(other);
return otherEngineeringCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5455,11 +5569,11 @@ void ParametricCRS::_exportToJSON(
// ---------------------------------------------------------------------------
bool ParametricCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherParametricCRS = dynamic_cast<const ParametricCRS *>(other);
return otherParametricCRS != nullptr &&
- SingleCRS::baseIsEquivalentTo(other, criterion);
+ SingleCRS::baseIsEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5560,11 +5674,11 @@ void DerivedVerticalCRS::_exportToPROJString(
// ---------------------------------------------------------------------------
bool DerivedVerticalCRS::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedVerticalCRS *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
@@ -5681,11 +5795,11 @@ void DerivedCRSTemplate<DerivedCRSTraits>::_exportToWKT(
template <class DerivedCRSTraits>
bool DerivedCRSTemplate<DerivedCRSTraits>::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDerivedCRS = dynamic_cast<const DerivedCRSTemplate *>(other);
return otherDerivedCRS != nullptr &&
- DerivedCRS::_isEquivalentTo(other, criterion);
+ DerivedCRS::_isEquivalentTo(other, criterion, dbContext);
}
//! @endcond
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index 321fe93f..d9d9c261 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -213,11 +213,13 @@ void Datum::setProperties(
// ---------------------------------------------------------------------------
-bool Datum::__isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+//! @cond Doxygen_Suppress
+bool Datum::_isEquivalentTo(const util::IComparable *other,
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDatum = dynamic_cast<const Datum *>(other);
if (otherDatum == nullptr ||
- !ObjectUsage::_isEquivalentTo(other, criterion)) {
+ !ObjectUsage::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if (criterion == util::IComparable::Criterion::STRICT) {
@@ -248,12 +250,13 @@ bool Datum::__isEquivalentTo(const util::IComparable *other,
}
if (conventionalRS() && otherDatum->conventionalRS() &&
conventionalRS()->_isEquivalentTo(
- otherDatum->conventionalRS().get(), criterion)) {
+ otherDatum->conventionalRS().get(), criterion, dbContext)) {
return false;
}
}
return true;
}
+//! @endcond
// ---------------------------------------------------------------------------
@@ -450,11 +453,11 @@ void PrimeMeridian::_exportToPROJString(
//! @cond Doxygen_Suppress
bool PrimeMeridian::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherPM = dynamic_cast<const PrimeMeridian *>(other);
if (otherPM == nullptr ||
- !IdentifiedObject::_isEquivalentTo(other, criterion)) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
// In MapInfo, the Paris prime meridian is returned as 2.3372291666667
@@ -984,11 +987,12 @@ EllipsoidNNPtr Ellipsoid::identify() const {
//! @cond Doxygen_Suppress
bool Ellipsoid::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherEllipsoid = dynamic_cast<const Ellipsoid *>(other);
if (otherEllipsoid == nullptr ||
(criterion == util::IComparable::Criterion::STRICT &&
- !IdentifiedObject::_isEquivalentTo(other, criterion))) {
+ !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext))) {
return false;
}
@@ -1197,7 +1201,8 @@ void GeodeticReferenceFrame::_exportToWKT(
io::WKTFormatter *formatter) const // throw(FormattingException)
{
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
- formatter->startNode(io::WKTConstants::DATUM, !identifiers().empty());
+ const auto &ids = identifiers();
+ formatter->startNode(io::WKTConstants::DATUM, !ids.empty());
auto l_name = nameStr();
if (l_name.empty()) {
l_name = "unnamed";
@@ -1232,10 +1237,35 @@ void GeodeticReferenceFrame::_exportToWKT(
}
}
}
- // Replace spaces by underscore, except if it is a special MapInfo
- // datum name
- } else if (!starts_with(l_name, "MIF ")) {
- l_name = io::WKTFormatter::morphNameToESRI(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";
}
@@ -1324,20 +1354,63 @@ void GeodeticReferenceFrame::_exportToJSON(
//! @cond Doxygen_Suppress
bool GeodeticReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherGRF = dynamic_cast<const GeodeticReferenceFrame *>(other);
- if (otherGRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherGRF == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return primeMeridian()->_isEquivalentTo(otherGRF->primeMeridian().get(),
- criterion) &&
- ellipsoid()->_isEquivalentTo(otherGRF->ellipsoid().get(), criterion);
+ 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<const GeodeticReferenceFrame *>(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{};
@@ -1407,11 +1480,11 @@ DynamicGeodeticReferenceFrame::deformationModelName() const {
//! @cond Doxygen_Suppress
bool DynamicGeodeticReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDGRF = dynamic_cast<const DynamicGeodeticReferenceFrame *>(other);
if (otherDGRF == nullptr ||
- !GeodeticReferenceFrame::_isEquivalentTo(other, criterion)) {
+ !GeodeticReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return frameReferenceEpoch()._isEquivalentTo(
@@ -1842,10 +1915,11 @@ void VerticalReferenceFrame::_exportToJSON(
//! @cond Doxygen_Suppress
bool VerticalReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherVRF = dynamic_cast<const VerticalReferenceFrame *>(other);
- if (otherVRF == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherVRF == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
if ((realizationMethod().has_value() ^
@@ -1932,11 +2006,11 @@ DynamicVerticalReferenceFrame::deformationModelName() const {
//! @cond Doxygen_Suppress
bool DynamicVerticalReferenceFrame::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherDGRF = dynamic_cast<const DynamicVerticalReferenceFrame *>(other);
if (otherDGRF == nullptr ||
- !VerticalReferenceFrame::_isEquivalentTo(other, criterion)) {
+ !VerticalReferenceFrame::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return frameReferenceEpoch()._isEquivalentTo(
@@ -2131,10 +2205,11 @@ void TemporalDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool TemporalDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const TemporalDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return temporalOrigin().toString() ==
@@ -2223,10 +2298,11 @@ void EngineeringDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool EngineeringDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const EngineeringDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return true;
@@ -2308,10 +2384,11 @@ void ParametricDatum::_exportToJSON(
//! @cond Doxygen_Suppress
bool ParametricDatum::_isEquivalentTo(
- const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherTD = dynamic_cast<const ParametricDatum *>(other);
- if (otherTD == nullptr || !Datum::_isEquivalentTo(other, criterion)) {
+ if (otherTD == nullptr ||
+ !Datum::_isEquivalentTo(other, criterion, dbContext)) {
return false;
}
return true;
diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp
index 96e62c16..7d15172e 100644
--- a/src/iso19111/factory.cpp
+++ b/src/iso19111/factory.cpp
@@ -264,6 +264,9 @@ struct DatabaseContext::Private {
std::map<std::string, std::vector<std::string>> cacheAllowedAuthorities_{};
+ lru11::Cache<std::string, std::list<std::string>> cacheAliasNames_{
+ CACHE_SIZE};
+
static void insertIntoCache(LRUCacheOfObjects &cache,
const std::string &code,
const util::BaseObjectPtr &obj);
@@ -1014,7 +1017,7 @@ bool DatabaseContext::isKnownName(const std::string &name,
/** \brief Gets the alias name from an official name.
*
- * @param officialName 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).
@@ -1046,6 +1049,63 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName,
// ---------------------------------------------------------------------------
+/** \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<std::string> 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<std::string> 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.
@@ -1349,9 +1409,17 @@ AuthorityFactory::AuthorityFactory(const DatabaseContextNNPtr &context,
AuthorityFactoryNNPtr
AuthorityFactory::create(const DatabaseContextNNPtr &context,
const std::string &authorityName) {
-
- auto factory = AuthorityFactory::nn_make_shared<AuthorityFactory>(
- context, authorityName);
+ const auto getFactory = [&context, &authorityName]() {
+ for (const auto &knownName : {"EPSG", "ESRI", "PROJ"}) {
+ if (ci_equal(authorityName, knownName)) {
+ return AuthorityFactory::nn_make_shared<AuthorityFactory>(
+ context, knownName);
+ }
+ }
+ return AuthorityFactory::nn_make_shared<AuthorityFactory>(
+ context, authorityName);
+ };
+ auto factory = getFactory();
factory->d->setThis(factory);
return factory;
}
@@ -5105,13 +5173,12 @@ static void addToListString(std::string &out, const char *in) {
out += in;
}
-static void addToListStringWithOR(std::string &out, const char *in) {
+static void addToListStringWithOR(std::string &out, const std::string &in) {
if (!out.empty()) {
out += " OR ";
}
out += in;
}
-
//! @endcond
// ---------------------------------------------------------------------------
@@ -5148,6 +5215,7 @@ AuthorityFactory::createObjectsFromName(
}
std::string sql(
+ "SELECT table_name, auth_name, code, name, deprecated FROM ("
"SELECT table_name, auth_name, code, name, deprecated FROM object_view "
"WHERE ");
if (deprecated) {
@@ -5163,108 +5231,141 @@ AuthorityFactory::createObjectsFromName(
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;
+ const auto getTableNameConstraint = [&allowedObjectTypes](
+ const std::string &colName) {
+ if (allowedObjectTypes.empty()) {
+ return colName + " 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,
+ "(" + colName + " = " GEOCENTRIC_SINGLE_QUOTED " AND "
+ "type = " GEOCENTRIC_SINGLE_QUOTED ")");
+ break;
+ case ObjectType::GEOGRAPHIC_CRS:
+ addToListStringWithOR(otherConditions,
+ "(" + colName +
+ " = 'geodetic_crs' AND "
+ "type IN (" GEOG_2D_SINGLE_QUOTED
+ "," GEOG_3D_SINGLE_QUOTED "))");
+ break;
+ case ObjectType::GEOGRAPHIC_2D_CRS:
+ addToListStringWithOR(
+ otherConditions,
+ "(" + colName + " = 'geodetic_crs' AND "
+ "type = " GEOG_2D_SINGLE_QUOTED ")");
+ break;
+ case ObjectType::GEOGRAPHIC_3D_CRS:
+ addToListStringWithOR(
+ otherConditions,
+ "(" + colName + " = '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;
+ std::string l_sql;
+ if (!tableNameList.empty()) {
+ l_sql = "((" + colName + " IN (";
+ l_sql += tableNameList;
+ l_sql += "))";
+ if (!otherConditions.empty()) {
+ l_sql += " OR ";
+ l_sql += otherConditions;
+ }
+ l_sql += ')';
+ } else if (!otherConditions.empty()) {
+ l_sql = "(";
+ l_sql += otherConditions;
+ l_sql += ')';
}
- sql += ')';
- } else if (!otherConditions.empty()) {
- sql += "(";
- sql += otherConditions;
- sql += ')';
+ return l_sql;
}
+ };
+
+ sql += getTableNameConstraint("table_name");
+
+ sql += " UNION SELECT ov.table_name AS table_name, "
+ "ov.auth_name AS auth_name, "
+ "ov.code AS code, a.alt_name AS name, "
+ "ov.deprecated AS deprecated FROM object_view ov "
+ "JOIN alias_name a ON ov.table_name = a.table_name AND "
+ "ov.auth_name = a.auth_name AND ov.code = a.code WHERE ";
+ if (deprecated) {
+ sql += "ov.deprecated = 1 AND ";
}
- sql += " ORDER BY deprecated, length(name), name";
+ if (!approximateMatch) {
+ sql += "a.alt_name LIKE ? AND ";
+ params.push_back(searchedNameWithoutDeprecated);
+ }
+ if (d->hasAuthorityRestriction()) {
+ sql += "ov.auth_name = ? AND ";
+ params.emplace_back(d->authority());
+ }
+ sql += getTableNameConstraint("ov.table_name");
+
+ sql += ") ORDER BY deprecated, length(name), name";
if (limitResultCount > 0 &&
limitResultCount <
static_cast<size_t>(std::numeric_limits<int>::max()) &&
@@ -5274,6 +5375,7 @@ AuthorityFactory::createObjectsFromName(
}
std::list<common::IdentifiedObjectNNPtr> res;
+ std::set<std::pair<std::string, std::string>> setIdentified;
// Querying geodetic datum is a super hot path when importing from WKT1
// so cache results.
@@ -5301,6 +5403,12 @@ AuthorityFactory::createObjectsFromName(
for (const auto &row : listOfRow) {
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
auto factory = d->createFactory(auth_name);
res.emplace_back(factory->createGeodeticDatum(code));
if (limitResultCount > 0 && res.size() == limitResultCount) {
@@ -5326,6 +5434,12 @@ AuthorityFactory::createObjectsFromName(
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
auto factory = d->createFactory(auth_name);
res.emplace_back(factory->createGeodeticDatum(code));
if (limitResultCount > 0 &&
@@ -5361,6 +5475,12 @@ AuthorityFactory::createObjectsFromName(
const auto &table_name = row[0];
const auto &auth_name = row[1];
const auto &code = row[2];
+ const auto key =
+ std::pair<std::string, std::string>(auth_name, code);
+ if (setIdentified.find(key) != setIdentified.end()) {
+ continue;
+ }
+ setIdentified.insert(key);
const auto &deprecatedStr = row[4];
if (isFirst) {
firstIsDeprecated = deprecatedStr == "1";
@@ -5526,6 +5646,7 @@ std::list<crs::GeodeticCRSNNPtr> AuthorityFactory::createGeodeticCRSFromDatum(
sql += " AND type = ?";
params.emplace_back(geodetic_crs_type);
}
+ sql += " ORDER BY auth_name, code";
auto sqlRes = d->run(sql, params);
std::list<crs::GeodeticCRSNNPtr> res;
for (const auto &row : sqlRes) {
@@ -5607,7 +5728,9 @@ static std::string buildSqlLookForAuthNameCode(
std::set<std::string> authorities;
for (const auto &crs : list) {
- const auto &ids = crs.first->identifiers();
+ auto boundCRS = dynamic_cast<crs::BoundCRS *>(crs.first.get());
+ const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers()
+ : crs.first->identifiers();
if (!ids.empty()) {
authorities.insert(*(ids[0]->codeSpace()));
}
@@ -5626,7 +5749,9 @@ static std::string buildSqlLookForAuthNameCode(
params.emplace_back(auth_name);
bool firstGeodCRSForAuth = true;
for (const auto &crs : list) {
- const auto &ids = crs.first->identifiers();
+ auto boundCRS = dynamic_cast<crs::BoundCRS *>(crs.first.get());
+ const auto &ids = boundCRS ? boundCRS->baseCRS()->identifiers()
+ : crs.first->identifiers();
if (!ids.empty() && *(ids[0]->codeSpace()) == auth_name) {
if (!firstGeodCRSForAuth) {
sql += ',';
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 2aec5fac..c013ce24 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -2051,12 +2051,27 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
auto res = authFactory->createObjectsFromName(
name, {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME},
true, 1);
- bool foundDatumName = false;
if (!res.empty()) {
+ bool foundDatumName = false;
const auto &refDatum = res.front();
if (metadata::Identifier::isEquivalentName(
name.c_str(), refDatum->nameStr().c_str())) {
foundDatumName = true;
+ } else if (refDatum->identifiers().size() == 1) {
+ const auto &id = refDatum->identifiers()[0];
+ const auto aliases =
+ authFactory->databaseContext()->getAliases(
+ *id->codeSpace(), id->code(), refDatum->nameStr(),
+ "geodetic_datum", std::string());
+ for (const auto &alias : aliases) {
+ if (metadata::Identifier::isEquivalentName(
+ name.c_str(), alias.c_str())) {
+ foundDatumName = true;
+ break;
+ }
+ }
+ }
+ if (foundDatumName) {
properties.set(IdentifiedObject::NAME_KEY,
refDatum->nameStr());
if (!properties.get(Identifier::CODESPACE_KEY) &&
@@ -2083,25 +2098,12 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame(
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(
- name, "geodetic_datum", std::string(), true, outTableName,
- authNameFromAlias, codeFromAlias);
- if (!officialName.empty()) {
- properties.set(IdentifiedObject::NAME_KEY, officialName);
- }
- }
}
}
@@ -2683,7 +2685,9 @@ WKTParser::Private::buildGeodeticCRS(const WKTNodeNNPtr &node) {
auto cs = buildCS(csNode, node, angularUnit);
auto ellipsoidalCS = nn_dynamic_pointer_cast<EllipsoidalCS>(cs);
if (ellipsoidalCS) {
- assert(!ci_equal(nodeName, WKTConstants::GEOCCS));
+ 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));
@@ -5669,13 +5673,18 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text,
DatabaseContextNNPtr dbContextNNPtr(NN_NO_CHECK(dbContext));
const auto &authName = tokens[0];
const auto &code = tokens[1];
- static const std::string epsg_lowercase("epsg");
- auto factory = AuthorityFactory::create(
- dbContextNNPtr,
- authName == epsg_lowercase ? Identifier::EPSG : authName);
+ 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)) {
@@ -7157,6 +7166,12 @@ void PROJStringFormatter::stopInversion() {
// the current end of steps
for (auto iter = startIter; iter != d->steps_.end(); ++iter) {
iter->inverted = !iter->inverted;
+ for (auto &paramValue : 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());
diff --git a/src/iso19111/metadata.cpp b/src/iso19111/metadata.cpp
index 41653b32..6266a86d 100644
--- a/src/iso19111/metadata.cpp
+++ b/src/iso19111/metadata.cpp
@@ -237,7 +237,8 @@ GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west,
//! @cond Doxygen_Suppress
bool GeographicBoundingBox::_isEquivalentTo(
- const util::IComparable *other, util::IComparable::Criterion) const {
+ const util::IComparable *other, util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other);
if (!otherExtent)
return false;
@@ -502,7 +503,8 @@ VerticalExtent::create(double minimumIn, double maximumIn,
//! @cond Doxygen_Suppress
bool VerticalExtent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion) const {
+ util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const VerticalExtent *>(other);
if (!otherExtent)
return false;
@@ -587,7 +589,8 @@ TemporalExtentNNPtr TemporalExtent::create(const std::string &start,
//! @cond Doxygen_Suppress
bool TemporalExtent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion) const {
+ util::IComparable::Criterion,
+ const io::DatabaseContextPtr &) const {
auto otherExtent = dynamic_cast<const TemporalExtent *>(other);
if (!otherExtent)
return false;
@@ -734,7 +737,8 @@ Extent::createFromBBOX(double west, double south, double east, double north,
//! @cond Doxygen_Suppress
bool Extent::_isEquivalentTo(const util::IComparable *other,
- util::IComparable::Criterion criterion) const {
+ util::IComparable::Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
auto otherExtent = dynamic_cast<const Extent *>(other);
bool ret =
(otherExtent &&
@@ -749,15 +753,18 @@ bool Extent::_isEquivalentTo(const util::IComparable *other,
if (ret) {
for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) {
ret = d->geographicElements_[i]->_isEquivalentTo(
- otherExtent->d->geographicElements_[i].get(), criterion);
+ 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);
+ 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);
+ otherExtent->d->temporalElements_[i].get(), criterion,
+ dbContext);
}
}
return ret;
diff --git a/src/iso19111/util.cpp b/src/iso19111/util.cpp
index 25207d5c..2a6178e2 100644
--- a/src/iso19111/util.cpp
+++ b/src/iso19111/util.cpp
@@ -687,11 +687,13 @@ IComparable::~IComparable() = default;
/** \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 {
- return _isEquivalentTo(other, criterion);
+bool IComparable::isEquivalentTo(
+ const IComparable *other, Criterion criterion,
+ const io::DatabaseContextPtr &dbContext) const {
+ return _isEquivalentTo(other, criterion, dbContext);
}
// ---------------------------------------------------------------------------
diff --git a/src/jniproj.cpp b/src/jniproj.cpp
deleted file mode 100644
index 6f441529..00000000
--- a/src/jniproj.cpp
+++ /dev/null
@@ -1,469 +0,0 @@
-/******************************************************************************
- * 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 <math.h>
-#include <string.h>
-#include "proj_internal.h"
-#include "org_proj4_PJ.h"
-#include <jni.h>
-
-#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<int>(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; i<length; i++) {
- axis[i] = pj->axis[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,<z>,...) 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<double*>(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
diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake
index d5a4d63e..704ece3d 100644
--- a/src/lib_proj.cmake
+++ b/src/lib_proj.cmake
@@ -240,7 +240,6 @@ set(SRC_LIBPROJ_CORE
initcache.cpp
internal.cpp
inv.cpp
- jniproj.cpp
list.cpp
log.cpp
malloc.cpp
@@ -319,26 +318,6 @@ source_group("CMake Files" FILES CMakeLists.txt)
add_definitions(-DPROJ_LIB="${CMAKE_INSTALL_PREFIX}/${DATADIR}")
#################################################
-## java wrapping with jni
-#################################################
-option(JNI_SUPPORT "Build support of java/jni wrapping for proj library" OFF)
-find_package(JNI QUIET)
-if(JNI_SUPPORT AND NOT JNI_FOUND)
- message(FATAL_ERROR "jni support is required but jni is not found")
-endif()
-boost_report_value(JNI_SUPPORT)
-if(JNI_SUPPORT)
- set(SRC_LIBPROJ_CORE
- ${SRC_LIBPROJ_CORE} jniproj.cpp)
- set(HEADERS_LIBPROJ
- ${HEADERS_LIBPROJ} org_proj4_PJ.h)
- source_group("Source Files\\JNI" FILES ${SRC_LIBPROJ_JNI})
- add_definitions(-DJNI_ENABLED)
- include_directories(${JNI_INCLUDE_DIRS})
- boost_report_value(JNI_INCLUDE_DIRS)
-endif()
-
-#################################################
## targets: libproj and proj_config.h
#################################################
set(ALL_LIBPROJ_SOURCES
@@ -455,7 +434,7 @@ if(CURL_FOUND)
target_link_libraries(${PROJ_CORE_TARGET} ${CURL_LIBRARY})
endif()
-if(MSVC)
+if(MSVC AND BUILD_LIBPROJ_SHARED)
target_compile_definitions(${PROJ_CORE_TARGET}
PRIVATE PROJ_MSVC_DLL_EXPORT=1)
endif()
diff --git a/src/networkfilemanager.cpp b/src/networkfilemanager.cpp
index 5d15c5af..64969d0f 100644
--- a/src/networkfilemanager.cpp
+++ b/src/networkfilemanager.cpp
@@ -1316,6 +1316,7 @@ std::unique_ptr<File> NetworkFile::open(PJ_CONTEXT *ctx, const char *filename) {
errorBuffer.resize(strlen(errorBuffer.data()));
pj_log(ctx, PJ_LOG_ERROR, "Cannot open %s: %s", filename,
errorBuffer.c_str());
+ pj_ctx_set_errno(ctx, PJD_ERR_NETWORK_ERROR);
}
bool ok = false;
@@ -1403,6 +1404,7 @@ size_t NetworkFile::read(void *buffer, size_t sizeBytes) {
&nRead, errorBuffer.size(), &errorBuffer[0],
m_ctx->networking.user_data);
if (!m_handle) {
+ pj_ctx_set_errno(m_ctx, PJD_ERR_NETWORK_ERROR);
return 0;
}
} else {
@@ -1418,6 +1420,7 @@ size_t NetworkFile::read(void *buffer, size_t sizeBytes) {
pj_log(m_ctx, PJ_LOG_ERROR, "Cannot read in %s: %s",
m_url.c_str(), errorBuffer.c_str());
}
+ pj_ctx_set_errno(m_ctx, PJD_ERR_NETWORK_ERROR);
return 0;
}
diff --git a/src/org_proj4_PJ.h b/src/org_proj4_PJ.h
deleted file mode 100644
index be5d3f58..00000000
--- a/src/org_proj4_PJ.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class org_proj4_PJ */
-
-#ifndef _Included_org_proj4_PJ
-#define _Included_org_proj4_PJ
-#ifdef __cplusplus
-extern "C" {
-#endif
-#undef org_proj4_PJ_DIMENSION_MAX
-#define org_proj4_PJ_DIMENSION_MAX 100L
-/*
- * Class: org_proj4_PJ
- * Method: allocatePJ
- * Signature: (Ljava/lang/String;)J
- */
-JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ
- (JNIEnv *, jclass, jstring);
-
-/*
- * Class: org_proj4_PJ
- * Method: allocateGeoPJ
- * Signature: (Lorg/proj4/PJ;)J
- */
-JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ
- (JNIEnv *, jclass, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getVersion
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion
- (JNIEnv *, jclass);
-
-/*
- * Class: org_proj4_PJ
- * Method: getDefinition
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getType
- * Signature: ()Lorg/proj4/PJ/Type;
- */
-JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getSemiMajorAxis
- * Signature: ()D
- */
-JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getSemiMinorAxis
- * Signature: ()D
- */
-JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getEccentricitySquared
- * Signature: ()D
- */
-JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getAxisDirections
- * Signature: ()[C
- */
-JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getGreenwichLongitude
- * Signature: ()D
- */
-JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: getLinearUnitToMetre
- * Signature: (Z)D
- */
-JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre
- (JNIEnv *, jobject, jboolean);
-
-/*
- * Class: org_proj4_PJ
- * Method: transform
- * Signature: (Lorg/proj4/PJ;I[DII)V
- */
-JNIEXPORT void JNICALL Java_org_proj4_PJ_transform
- (JNIEnv *, jobject, jobject, jint, jdoubleArray, jint, jint);
-
-/*
- * Class: org_proj4_PJ
- * Method: getLastError
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: toString
- * Signature: ()Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString
- (JNIEnv *, jobject);
-
-/*
- * Class: org_proj4_PJ
- * Method: finalize
- * Signature: ()V
- */
-JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize
- (JNIEnv *, jobject);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
diff --git a/src/pipeline.cpp b/src/pipeline.cpp
index f65dbfa0..511a69fe 100644
--- a/src/pipeline.cpp
+++ b/src/pipeline.cpp
@@ -170,6 +170,9 @@ static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) {
if( !step.omit_fwd )
{
point = proj_trans (step.pj, PJ_FWD, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
@@ -186,6 +189,9 @@ static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) {
if( !step.omit_inv )
{
point = proj_trans (step.pj, PJ_INV, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
@@ -204,6 +210,9 @@ static PJ_XYZ pipeline_forward_3d (PJ_LPZ lpz, PJ *P) {
if( !step.omit_fwd )
{
point = pj_approx_3D_trans (step.pj, PJ_FWD, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
@@ -222,6 +231,9 @@ static PJ_LPZ pipeline_reverse_3d (PJ_XYZ xyz, PJ *P) {
if( !step.omit_inv )
{
point = proj_trans (step.pj, PJ_INV, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
@@ -240,6 +252,9 @@ static PJ_XY pipeline_forward (PJ_LP lp, PJ *P) {
if( !step.omit_fwd )
{
point = pj_approx_2D_trans (step.pj, PJ_FWD, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
@@ -258,6 +273,9 @@ static PJ_LP pipeline_reverse (PJ_XY xy, PJ *P) {
if( !step.omit_inv )
{
point = pj_approx_2D_trans (step.pj, PJ_INV, point);
+ if( point.xyzt.x == HUGE_VAL ) {
+ break;
+ }
}
}
diff --git a/src/proj.h b/src/proj.h
index 2a05fbc9..fbed71b0 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -152,8 +152,8 @@ extern "C" {
#endif
/* The version numbers should be updated with every release! **/
-#define PROJ_VERSION_MAJOR 6
-#define PROJ_VERSION_MINOR 3
+#define PROJ_VERSION_MAJOR 7
+#define PROJ_VERSION_MINOR 0
#define PROJ_VERSION_PATCH 0
extern char const PROJ_DLL pj_release[]; /* global release id string */
@@ -1002,6 +1002,10 @@ PJ_OBJ_LIST PROJ_DLL *proj_get_non_deprecated(PJ_CONTEXT *ctx,
int PROJ_DLL proj_is_equivalent_to(const PJ *obj, const PJ *other,
PJ_COMPARISON_CRITERION criterion);
+int PROJ_DLL proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx,
+ const PJ *obj, const PJ *other,
+ PJ_COMPARISON_CRITERION criterion);
+
int PROJ_DLL proj_is_crs(const PJ *obj);
const char PROJ_DLL* proj_get_name(const PJ *obj);
@@ -1257,6 +1261,9 @@ int PROJ_DLL proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx,
int value_count,
int emit_error_if_incompatible);
+PJ PROJ_DLL *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj);
+
+
int PROJ_DLL proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
const PJ *concatoperation);
diff --git a/src/proj_api.h b/src/proj_api.h
index 852cb14c..24046026 100644
--- a/src/proj_api.h
+++ b/src/proj_api.h
@@ -38,7 +38,7 @@
#endif
#ifndef PJ_VERSION
-#define PJ_VERSION 630
+#define PJ_VERSION 700
#endif
#ifdef PROJ_RENAME_SYMBOLS
diff --git a/src/proj_constants.h b/src/proj_constants.h
index 5c642862..21cd87fb 100644
--- a/src/proj_constants.h
+++ b/src/proj_constants.h
@@ -513,6 +513,9 @@
#define EPSG_CODE_METHOD_VERTCON 9658
#define EPSG_NAME_METHOD_VERTCON "VERTCON"
+#define EPSG_CODE_METHOD_VERTICALGRID_NZLVD 1071
+#define EPSG_NAME_METHOD_VERTICALGRID_NZLVD "Vertical Offset by Grid Interpolation (NZLVD)"
+
#define EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE "Vertical offset file"
#define EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE 8732
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 15b98859..bb0e3453 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -658,6 +658,7 @@ struct FACTORS {
#define PJD_ERR_INCONSISTENT_UNIT -59
#define PJD_ERR_MUTUALLY_EXCLUSIVE_ARGS -60
#define PJD_ERR_GENERIC_ERROR -61
+#define PJD_ERR_NETWORK_ERROR -62
/* NOTE: Remember to update src/strerrno.cpp, src/apps/gie.cpp and transient_error in */
/* src/transform.cpp when adding new value */
diff --git a/src/release.cpp b/src/release.cpp
index 425bdb9a..648c5398 100644
--- a/src/release.cpp
+++ b/src/release.cpp
@@ -11,7 +11,7 @@ char const pj_release[] =
STR(PROJ_VERSION_MAJOR)"."
STR(PROJ_VERSION_MINOR)"."
STR(PROJ_VERSION_PATCH)", "
- "January 1st, 2020";
+ "March 1st, 2020";
const char *pj_get_release() {
return pj_release;
diff --git a/src/strerrno.cpp b/src/strerrno.cpp
index 9bf5f45a..5ae0d7e1 100644
--- a/src/strerrno.cpp
+++ b/src/strerrno.cpp
@@ -71,6 +71,7 @@ pj_err_list[] = {
"inconsistent unit type between input and output", /* -59 */
"arguments are mutually exclusive", /* -60 */
"generic error of unknown origin", /* -61 */
+ "network error", /* -62 */
/* When adding error messages, remember to update ID defines in
projects.h, and transient_error array in pj_transform */
diff --git a/src/transform.cpp b/src/transform.cpp
index 811e2a6a..ea3d9ae2 100644
--- a/src/transform.cpp
+++ b/src/transform.cpp
@@ -234,6 +234,14 @@ static int geographic_to_projected (PJ *P, long n, int dist, double *x, double *
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 <n; i++ )
{
PJ_XY projected_loc;
@@ -345,6 +353,14 @@ static int projected_to_geographic (PJ *P, long n, int dist, double *x, double *
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;
diff --git a/src/transformations/helmert.cpp b/src/transformations/helmert.cpp
index 7a3e64d0..2045dea2 100644
--- a/src/transformations/helmert.cpp
+++ b/src/transformations/helmert.cpp
@@ -665,18 +665,18 @@ PJ *TRANSFORMATION(helmert, 0) {
}
/* Let's help with debugging */
- if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) {
- proj_log_debug(P, "Helmert parameters:");
- proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z);
- proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f",
+ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) {
+ proj_log_trace(P, "Helmert parameters:");
+ proj_log_trace(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z);
+ proj_log_trace(P, "rx= %8.5f ry= %8.5f rz= %8.5f",
Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD);
- proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact,
+ proj_log_trace(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact,
Q->no_rotation ? "" :
Q->is_position_vector ? " convention=position_vector" :
" convention=coordinate_frame");
- proj_log_debug(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z);
- proj_log_debug(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k);
- proj_log_debug(P, "ds= %8.5f t_epoch=%8.5f", Q->dscale, Q->t_epoch);
+ proj_log_trace(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z);
+ proj_log_trace(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k);
+ proj_log_trace(P, "ds= %8.5f t_epoch=%8.5f", Q->dscale, Q->t_epoch);
}
if (Q->no_rotation) {
@@ -729,15 +729,15 @@ PJ *TRANSFORMATION(molobadekas, 0) {
/* Let's help with debugging */
- if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) {
- proj_log_debug(P, "Molodensky-Badekas parameters:");
- proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z);
- proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f",
+ if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) {
+ proj_log_trace(P, "Molodensky-Badekas parameters:");
+ proj_log_trace(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z);
+ proj_log_trace(P, "rx= %8.5f ry= %8.5f rz= %8.5f",
Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD);
- proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact,
+ proj_log_trace(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact,
Q->is_position_vector ? " convention=position_vector" :
" convention=coordinate_frame");
- proj_log_debug(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z);
+ proj_log_trace(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z);
}
/* as an optimization, we incorporate the refp in the translation terms */