aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2019-05-07 11:39:48 +0200
committerGitHub <noreply@github.com>2019-05-07 11:39:48 +0200
commit850050693a25843d6ae69492cfad72f7753e39f7 (patch)
tree4d464df04190ae115e7f54259457286d22580eb9
parent4636df33ed4d2a9bedf19973d58a42858fb816c0 (diff)
parentd4ffaca08a4f2ef3475165c2634561ee9bf01885 (diff)
downloadPROJ-850050693a25843d6ae69492cfad72f7753e39f7.tar.gz
PROJ-850050693a25843d6ae69492cfad72f7753e39f7.zip
Merge pull request #1454 from rouault/fix_ETRS89_3D_to_proj_string_with_geoidgrids_nadgrids
createOperations(): fix case of ETRS89 3D to proj string with nadgrids and geoidgrids
-rw-r--r--data/Makefile.am4
-rw-r--r--data/sql/customizations.sql19
-rw-r--r--data/sql/grid_alternatives.sql38
-rw-r--r--include/proj/coordinateoperation.hpp3
-rw-r--r--include/proj/internal/internal.hpp3
-rw-r--r--include/proj/io.hpp3
-rw-r--r--scripts/reference_exported_symbols.txt3
-rw-r--r--src/iso19111/coordinateoperation.cpp142
-rw-r--r--src/iso19111/internal.cpp15
-rw-r--r--src/iso19111/io.cpp20
-rw-r--r--test/unit/test_operation.cpp57
-rwxr-xr-xtravis/osx/before_install.sh1
12 files changed, 270 insertions, 38 deletions
diff --git a/data/Makefile.am b/data/Makefile.am
index a4446287..1bf95cd2 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -64,7 +64,7 @@ proj.db: $(DATAPATH)/sql/*.sql
for x in $(SQL_ORDERED_LIST); do \
export SQL_EXPANDED_LIST="$${SQL_EXPANDED_LIST} $(DATAPATH)/$$x"; \
done; \
- if test "x$(PROJ_DB_CACHE_DIR)" != "x" -a -f "$(PROJ_DB_CACHE_DIR)/proj.db" -a -f "$(PROJ_DB_CACHE_DIR)/proj.db.sql.md5" ; then \
+ if test "x$(PROJ_DB_CACHE_DIR)" != "x" -a -x "$$(command -v md5sum)" -a -f "$(PROJ_DB_CACHE_DIR)/proj.db" -a -f "$(PROJ_DB_CACHE_DIR)/proj.db.sql.md5" ; then \
cat $${SQL_EXPANDED_LIST} | md5sum | diff - "$(PROJ_DB_CACHE_DIR)/proj.db.sql.md5" > /dev/null \
&& (echo "Reusing cached proj.db"; cp "$(PROJ_DB_CACHE_DIR)/proj.db" proj.db); \
fi; \
@@ -84,7 +84,7 @@ proj.db: $(DATAPATH)/sql/*.sql
$(RM) proj.db; \
exit 1; \
else \
- if test "x$(PROJ_DB_CACHE_DIR)" != "x" ; then \
+ if test "x$(PROJ_DB_CACHE_DIR)" != "x" -a -x "$$(command -v md5sum)" ; then \
mkdir -p "$(PROJ_DB_CACHE_DIR)"; \
cat $${SQL_EXPANDED_LIST} | md5sum > "$(PROJ_DB_CACHE_DIR)/proj.db.sql.md5"; \
cp proj.db "$(PROJ_DB_CACHE_DIR)"; \
diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql
index ee023700..0ce4a692 100644
--- a/data/sql/customizations.sql
+++ b/data/sql/customizations.sql
@@ -19,6 +19,25 @@ DELETE FROM "supersession" WHERE superseded_table_name = 'grid_transformation' A
replacement_code = '8885' AND
source = 'EPSG';
+-- ('EPSG','7001','ETRS89 to NAP height (1)') lacks an interpolationCRS with Amersfoort / EPSG:4289
+-- See https://salsa.debian.org/debian-gis-team/proj-rdnap/blob/debian/2008-8/Use%20of%20RDTRANS2008%20and%20NAPTRANS2008.pdf
+-- "The naptrans2008 VDatum-grid is referenced to the Bessel-1841 ellipsoid"
+CREATE TABLE dummy(foo);
+CREATE TRIGGER check_grid_transformation_epsg_7001
+BEFORE INSERT ON dummy
+FOR EACH ROW BEGIN
+ SELECT RAISE(ABORT, 'grid_transformation EPSG:7001 entry is not ETRS89 to NAP height (1)')
+ WHERE NOT EXISTS(SELECT 1 FROM grid_transformation WHERE auth_name = 'EPSG' AND code = '7001' AND name = 'ETRS89 to NAP height (1)');
+ SELECT RAISE(ABORT, 'grid_transformation EPSG:7001 entry has already an interpolationCRS')
+ WHERE EXISTS(SELECT 1 FROM grid_transformation WHERE auth_name = 'EPSG' AND code = '7001' AND interpolation_crs_auth_name IS NOT NULL);
+END;
+INSERT INTO dummy DEFAULT VALUES;
+DROP TRIGGER check_grid_transformation_epsg_7001;
+DROP TABLE dummy;
+UPDATE grid_transformation SET interpolation_crs_auth_name = 'EPSG',
+ interpolation_crs_code = '4289'
+ WHERE auth_name = 'EPSG' AND code = '7001';
+
-- Define the allowed authorities, and their precedence, when researching a
-- coordinate operation
diff --git a/data/sql/grid_alternatives.sql b/data/sql/grid_alternatives.sql
index df82ddd3..38560202 100644
--- a/data/sql/grid_alternatives.sql
+++ b/data/sql/grid_alternatives.sql
@@ -798,3 +798,41 @@ INSERT INTO grid_alternatives(original_grid_name,
0,
'proj-datumgrid-oceania',
NULL, NULL, NULL, NULL);
+
+-- Netherlands / RDNAP (non-free grids)
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('naptrans2008.gtx',
+ 'naptrans2008.gtx',
+ 'GTX',
+ 'vgridshift',
+ 0,
+ NULL, -- package name
+ 'https://salsa.debian.org/debian-gis-team/proj-rdnap/raw/upstream/2008/naptrans2008.gtx',
+ 1, -- direct download
+ 0, -- non-freely licensed. See https://salsa.debian.org/debian-gis-team/proj-rdnap/raw/master/debian/copyright
+ NULL);
+
+INSERT INTO grid_alternatives(original_grid_name,
+ proj_grid_name,
+ proj_grid_format,
+ proj_method,
+ inverse_direction,
+ package_name,
+ url, direct_download, open_license, directory)
+ VALUES ('rdtrans2008.gsb',
+ 'rdtrans2008.gsb',
+ 'NTv2',
+ 'hgridshift',
+ 0,
+ NULL, -- package name
+ 'https://salsa.debian.org/debian-gis-team/proj-rdnap/raw/upstream/2008/rdtrans2008.gsb',
+ 1, -- direct download
+ 0, -- non-freely licensed. See https://salsa.debian.org/debian-gis-team/proj-rdnap/raw/master/debian/copyright
+ NULL);
diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp
index 994cbab2..7ade26f2 100644
--- a/include/proj/coordinateoperation.hpp
+++ b/include/proj/coordinateoperation.hpp
@@ -1476,7 +1476,8 @@ class PROJ_GCC_DLL Transformation : public SingleOperation {
PROJ_DLL static TransformationNNPtr
createGravityRelatedHeightToGeographic3D(
const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn,
- const crs::CRSNNPtr &targetCRSIn, const std::string &filename,
+ const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn,
+ const std::string &filename,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies);
PROJ_DLL static TransformationNNPtr createVERTCON(
diff --git a/include/proj/internal/internal.hpp b/include/proj/internal/internal.hpp
index 220c137b..2222a264 100644
--- a/include/proj/internal/internal.hpp
+++ b/include/proj/internal/internal.hpp
@@ -140,6 +140,9 @@ std::string toupper(const std::string &osStr);
PROJ_FOR_TEST std::vector<std::string> split(const std::string &osStr,
char separator);
+PROJ_FOR_TEST std::vector<std::string> split(const std::string &osStr,
+ const std::string &separator);
+
bool ci_equal(const char *a, const char *b) noexcept;
#ifdef SUPPORT_DELETED_FUNCTION
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index a603533e..c553598d 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -427,7 +427,8 @@ class PROJ_GCC_DLL PROJStringFormatter {
PROJ_INTERNAL void setOmitProjLongLatIfPossible(bool omit);
PROJ_INTERNAL bool omitProjLongLatIfPossible() const;
- PROJ_INTERNAL void setOmitZUnitConversion(bool omit);
+ PROJ_INTERNAL void pushOmitZUnitConversion();
+ PROJ_INTERNAL void popOmitZUnitConversion();
PROJ_INTERNAL bool omitZUnitConversion() const;
PROJ_INTERNAL void setDropEarlyBindingsTerms(bool drop);
diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt
index 5d774eef..fe217812 100644
--- a/scripts/reference_exported_symbols.txt
+++ b/scripts/reference_exported_symbols.txt
@@ -279,6 +279,7 @@ osgeo::proj::internal::ci_find(std::string const&, char const*)
osgeo::proj::internal::c_locale_stod(std::string const&)
osgeo::proj::internal::replaceAll(std::string const&, std::string const&, std::string const&)
osgeo::proj::internal::split(std::string const&, char)
+osgeo::proj::internal::split(std::string const&, std::string const&)
osgeo::proj::internal::tolower(std::string const&)
osgeo::proj::internal::toString(double, int)
osgeo::proj::io::AuthorityFactory::~AuthorityFactory()
@@ -618,7 +619,7 @@ osgeo::proj::operation::Transformation::createGeocentricTranslations(osgeo::proj
osgeo::proj::operation::Transformation::createGeographic2DOffsets(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Angle const&, osgeo::proj::common::Angle const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createGeographic2DWithHeightOffsets(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Angle const&, osgeo::proj::common::Angle const&, osgeo::proj::common::Length const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createGeographic3DOffsets(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Angle const&, osgeo::proj::common::Angle const&, osgeo::proj::common::Length const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
-osgeo::proj::operation::Transformation::createGravityRelatedHeightToGeographic3D(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, std::string const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
+osgeo::proj::operation::Transformation::createGravityRelatedHeightToGeographic3D(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, std::shared_ptr<osgeo::proj::crs::CRS> const&, std::string const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createLongitudeRotation(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, osgeo::proj::common::Angle const&)
osgeo::proj::operation::Transformation::createMolodensky(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, double, double, double, double, double, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
osgeo::proj::operation::Transformation::createNTv2(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::crs::CRS> > const&, std::string const&, std::vector<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> >, std::allocator<dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::metadata::PositionalAccuracy> > > > const&)
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 605004b6..348a776a 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -6958,11 +6958,11 @@ TransformationNNPtr Transformation::createNTv2(
static TransformationNNPtr _createGravityRelatedHeightToGeographic3D(
const util::PropertyMap &properties, bool inverse,
const crs::CRSNNPtr &sourceCRSIn, const crs::CRSNNPtr &targetCRSIn,
- const std::string &filename,
+ const crs::CRSPtr &interpolationCRSIn, const std::string &filename,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
return Transformation::create(
- properties, sourceCRSIn, targetCRSIn, nullptr,
+ properties, sourceCRSIn, targetCRSIn, interpolationCRSIn,
util::PropertyMap().set(
common::IdentifiedObject::NAME_KEY,
inverse ? INVERSE_OF + PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D
@@ -6981,17 +6981,20 @@ static TransformationNNPtr _createGravityRelatedHeightToGeographic3D(
* At minimum the name should be defined.
* @param sourceCRSIn Source CRS.
* @param targetCRSIn Target CRS.
+ * @param interpolationCRSIn Interpolation CRS. (might be null)
* @param filename GRID filename.
* @param accuracies Vector of positional accuracy (might be empty).
* @return new Transformation.
*/
TransformationNNPtr Transformation::createGravityRelatedHeightToGeographic3D(
const util::PropertyMap &properties, const crs::CRSNNPtr &sourceCRSIn,
- const crs::CRSNNPtr &targetCRSIn, const std::string &filename,
+ const crs::CRSNNPtr &targetCRSIn, const crs::CRSPtr &interpolationCRSIn,
+ const std::string &filename,
const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
return _createGravityRelatedHeightToGeographic3D(
- properties, false, sourceCRSIn, targetCRSIn, filename, accuracies);
+ properties, false, sourceCRSIn, targetCRSIn, interpolationCRSIn,
+ filename, accuracies);
}
// ---------------------------------------------------------------------------
@@ -7302,8 +7305,20 @@ createPropertiesForInverse(const CoordinateOperation *op, bool derivedFrom,
auto targetCRS = op->targetCRS();
std::string name;
if (!forwardName.empty()) {
- if (starts_with(forwardName, INVERSE_OF)) {
- name = forwardName.substr(INVERSE_OF.size());
+ if (starts_with(forwardName, INVERSE_OF) ||
+ forwardName.find(" + ") != std::string::npos) {
+ auto tokens = split(forwardName, " + ");
+ for (size_t i = tokens.size(); i > 0;) {
+ i--;
+ if (!name.empty()) {
+ name += " + ";
+ }
+ if (starts_with(tokens[i], INVERSE_OF)) {
+ name += tokens[i].substr(INVERSE_OF.size());
+ } else {
+ name += INVERSE_OF + tokens[i];
+ }
+ }
} else if (!sourceCRS || !targetCRS ||
forwardName != buildOpName(opType, sourceCRS, targetCRS)) {
name = INVERSE_OF + forwardName;
@@ -8195,13 +8210,14 @@ TransformationNNPtr Transformation::substitutePROJAlternativeGridNames(
return createGravityRelatedHeightToGeographic3D(
createPropertiesForInverse(self.as_nullable().get(),
true, false),
- targetCRS(), sourceCRS(), projFilename,
- coordinateOperationAccuracies())
+ targetCRS(), sourceCRS(), interpolationCRS(),
+ projFilename, coordinateOperationAccuracies())
->inverseAsTransformation();
} else {
return createGravityRelatedHeightToGeographic3D(
createSimilarPropertiesTransformation(self), sourceCRS(),
- targetCRS(), projFilename, coordinateOperationAccuracies());
+ targetCRS(), interpolationCRS(), projFilename,
+ coordinateOperationAccuracies());
}
}
}
@@ -10972,19 +10988,19 @@ struct MyPROJStringExportableHorizVertical final
// cppcheck-suppress functionStatic
_exportToPROJString(io::PROJStringFormatter *formatter) const override {
- formatter->setOmitZUnitConversion(true);
+ formatter->pushOmitZUnitConversion();
horizTransform->_exportToPROJString(formatter);
formatter->startInversion();
geogDst->addAngularUnitConvertAndAxisSwap(formatter);
formatter->stopInversion();
- formatter->setOmitZUnitConversion(false);
+ formatter->popOmitZUnitConversion();
verticalTransform->_exportToPROJString(formatter);
- formatter->setOmitZUnitConversion(true);
+ formatter->pushOmitZUnitConversion();
geogDst->addAngularUnitConvertAndAxisSwap(formatter);
- formatter->setOmitZUnitConversion(false);
+ formatter->popOmitZUnitConversion();
}
};
@@ -11016,7 +11032,7 @@ struct MyPROJStringExportableHorizVerticalHorizPROJBased final
// cppcheck-suppress functionStatic
_exportToPROJString(io::PROJStringFormatter *formatter) const override {
- formatter->setOmitZUnitConversion(true);
+ formatter->pushOmitZUnitConversion();
opSrcCRSToGeogCRS->_exportToPROJString(formatter);
@@ -11024,17 +11040,17 @@ struct MyPROJStringExportableHorizVerticalHorizPROJBased final
interpolationGeogCRS->addAngularUnitConvertAndAxisSwap(formatter);
formatter->stopInversion();
- formatter->setOmitZUnitConversion(false);
+ formatter->popOmitZUnitConversion();
verticalTransform->_exportToPROJString(formatter);
- formatter->setOmitZUnitConversion(true);
+ formatter->pushOmitZUnitConversion();
interpolationGeogCRS->addAngularUnitConvertAndAxisSwap(formatter);
opGeogCRStoDstCRS->_exportToPROJString(formatter);
- formatter->setOmitZUnitConversion(false);
+ formatter->popOmitZUnitConversion();
}
};
@@ -12135,6 +12151,37 @@ CoordinateOperationFactory::Private::createOperations(
}
}
+ auto vertCRSOfBaseOfBoundSrc =
+ dynamic_cast<const crs::VerticalCRS *>(boundSrc->baseCRS().get());
+ if (vertCRSOfBaseOfBoundSrc && hubSrcGeog &&
+ hubSrcGeog->coordinateSystem()->axisList().size() == 3 &&
+ geogDst->coordinateSystem()->axisList().size() == 3) {
+ auto opsFirst = createOperations(sourceCRS, hubSrc, context);
+ auto opsSecond = createOperations(hubSrc, targetCRS, context);
+ if (!opsFirst.empty() && !opsSecond.empty()) {
+ for (const auto &opFirst : opsFirst) {
+ for (const auto &opLast : opsSecond) {
+ // Exclude artificial transformations from the hub
+ // to the target CRS
+ if (!opLast->hasBallparkTransformation()) {
+ try {
+ res.emplace_back(
+ ConcatenatedOperation::
+ createComputeMetadata(
+ {opFirst, opLast},
+ !allowEmptyIntersection));
+ } catch (
+ const InvalidOperationEmptyIntersection &) {
+ }
+ }
+ }
+ }
+ if (!res.empty()) {
+ return res;
+ }
+ }
+ }
+
return createOperations(boundSrc->baseCRS(), targetCRS, context);
}
@@ -12349,7 +12396,8 @@ CoordinateOperationFactory::Private::createOperations(
const auto &componentsSrc = compoundSrc->componentReferenceSystems();
if (!componentsSrc.empty()) {
std::vector<CoordinateOperationNNPtr> horizTransforms;
- if (componentsSrc[0]->extractGeographicCRS()) {
+ auto srcGeogCRS = componentsSrc[0]->extractGeographicCRS();
+ if (srcGeogCRS) {
horizTransforms =
createOperations(componentsSrc[0], targetCRS, context);
}
@@ -12363,11 +12411,61 @@ CoordinateOperationFactory::Private::createOperations(
for (const auto &horizTransform : horizTransforms) {
for (const auto &verticalTransform : verticalTransforms) {
- auto op = createHorizVerticalPROJBased(
- sourceCRS, targetCRS, horizTransform,
- verticalTransform);
+ crs::GeographicCRSPtr interpolationGeogCRS;
+ auto transformationVerticalTransform =
+ dynamic_cast<const Transformation *>(
+ verticalTransform.get());
+ if (transformationVerticalTransform) {
+ auto interpTransformCRS =
+ transformationVerticalTransform
+ ->interpolationCRS();
+ if (interpTransformCRS) {
+ auto nn_interpTransformCRS =
+ NN_NO_CHECK(interpTransformCRS);
+ if (dynamic_cast<const crs::GeographicCRS *>(
+ nn_interpTransformCRS.get())) {
+ interpolationGeogCRS =
+ util::nn_dynamic_pointer_cast<
+ crs::GeographicCRS>(
+ nn_interpTransformCRS);
+ }
+ }
+ }
+ bool done = false;
+ if (interpolationGeogCRS &&
+ (interpolationGeogCRS->_isEquivalentTo(
+ srcGeogCRS.get(),
+ util::IComparable::Criterion::EQUIVALENT) ||
+ interpolationGeogCRS->is2DPartOf3D(
+ NN_NO_CHECK(srcGeogCRS.get())))) {
+ auto srcToInterp = createOperations(
+ componentsSrc[0],
+ NN_NO_CHECK(interpolationGeogCRS), context);
+ auto interpToCompoundHoriz = createOperations(
+ NN_NO_CHECK(interpolationGeogCRS),
+ componentsSrc[0], context);
+ if (!srcToInterp.empty() &&
+ !interpToCompoundHoriz.empty()) {
+ auto op = createHorizVerticalHorizPROJBased(
+ sourceCRS, componentsSrc[0],
+ srcToInterp.front(), verticalTransform,
+ interpToCompoundHoriz.front(),
+ interpolationGeogCRS);
+ done = true;
+ res.emplace_back(
+ ConcatenatedOperation::
+ createComputeMetadata(
+ {op, horizTransform},
+ !allowEmptyIntersection));
+ }
+ }
+ if (!done) {
+ auto op = createHorizVerticalPROJBased(
+ sourceCRS, targetCRS, horizTransform,
+ verticalTransform);
- res.emplace_back(op);
+ res.emplace_back(op);
+ }
}
}
return res;
diff --git a/src/iso19111/internal.cpp b/src/iso19111/internal.cpp
index 240c98f4..4810202d 100644
--- a/src/iso19111/internal.cpp
+++ b/src/iso19111/internal.cpp
@@ -298,6 +298,21 @@ std::vector<std::string> split(const std::string &str, char separator) {
// ---------------------------------------------------------------------------
+std::vector<std::string> split(const std::string &str,
+ const std::string &separator) {
+ std::vector<std::string> res;
+ size_t lastPos = 0;
+ size_t newPos = 0;
+ while ((newPos = str.find(separator, lastPos)) != std::string::npos) {
+ res.push_back(str.substr(lastPos, newPos - lastPos));
+ lastPos = newPos + separator.size();
+ }
+ res.push_back(str.substr(lastPos));
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
#ifdef _WIN32
// For some reason, sqlite3_snprintf() in the sqlite3 builds used on AppVeyor
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index e072a66f..c84ea2f3 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -3818,7 +3818,7 @@ CRSNNPtr WKTParser::Private::buildVerticalCRS(const WKTNodeNNPtr &node) {
Transformation::createGravityRelatedHeightToGeographic3D(
PropertyMap().set(IdentifiedObject::NAME_KEY,
transformationName),
- crs, GeographicCRS::EPSG_4979,
+ crs, GeographicCRS::EPSG_4979, nullptr,
stripQuotes(extensionChildren[1]),
std::vector<PositionalAccuracyNNPtr>());
return nn_static_pointer_cast<CRS>(BoundCRS::create(
@@ -4922,7 +4922,7 @@ struct PROJStringFormatter::Private {
};
std::vector<InversionStackElt> inversionStack_{InversionStackElt()};
bool omitProjLongLatIfPossible_ = false;
- bool omitZUnitConversion_ = false;
+ std::vector<bool> omitZUnitConversion_{false};
DatabaseContextPtr dbContext_{};
bool useApproxTMerc_ = false;
bool addNoDefs_ = true;
@@ -5939,15 +5939,21 @@ bool PROJStringFormatter::omitProjLongLatIfPossible() const {
// ---------------------------------------------------------------------------
-void PROJStringFormatter::setOmitZUnitConversion(bool omit) {
- assert(d->omitZUnitConversion_ ^ omit);
- d->omitZUnitConversion_ = omit;
+void PROJStringFormatter::pushOmitZUnitConversion() {
+ d->omitZUnitConversion_.push_back(true);
+}
+
+// ---------------------------------------------------------------------------
+
+void PROJStringFormatter::popOmitZUnitConversion() {
+ assert(d->omitZUnitConversion_.size() > 1);
+ d->omitZUnitConversion_.pop_back();
}
// ---------------------------------------------------------------------------
bool PROJStringFormatter::omitZUnitConversion() const {
- return d->omitZUnitConversion_;
+ return d->omitZUnitConversion_.back();
}
// ---------------------------------------------------------------------------
@@ -6995,7 +7001,7 @@ PROJStringParser::Private::buildBoundOrCompoundCRSIfNeeded(int iStep,
Transformation::createGravityRelatedHeightToGeographic3D(
PropertyMap().set(IdentifiedObject::NAME_KEY,
"unknown to WGS84 ellipsoidal height"),
- crs, GeographicCRS::EPSG_4979, geoidgrids,
+ crs, GeographicCRS::EPSG_4979, nullptr, geoidgrids,
std::vector<PositionalAccuracyNNPtr>());
auto boundvcrs =
BoundCRS::create(vcrs, GeographicCRS::EPSG_4979, transformation);
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index a38e9df2..5ac1609d 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5903,6 +5903,35 @@ TEST(operation, boundCRS_with_basecrs_with_extent_to_geogCRS) {
// ---------------------------------------------------------------------------
+TEST(operation, ETRS89_3D_to_proj_string_with_geoidgrids_nadgrids) {
+ auto authFactory =
+ AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ // ETRS89 3D
+ auto src = authFactory->createCoordinateReferenceSystem("4937");
+ auto objDst = PROJStringParser().createFromPROJString(
+ "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 "
+ "+k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel "
+ "+nadgrids=rdtrans2008.gsb +geoidgrids=naptrans2008.gtx +units=m "
+ "+type=crs");
+ auto dst = nn_dynamic_pointer_cast<CRS>(objDst);
+ ASSERT_TRUE(dst != nullptr);
+ auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0);
+ auto list = CoordinateOperationFactory::create()->createOperations(
+ src, NN_NO_CHECK(dst), ctxt);
+ ASSERT_EQ(list.size(), 1U);
+ EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()),
+ "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=axisswap +order=2,1 "
+ "+step +inv +proj=vgridshift +grids=naptrans2008.gtx "
+ "+multiplier=1 "
+ "+step +inv +proj=hgridshift +grids=rdtrans2008.gsb "
+ "+step +proj=sterea +lat_0=52.1561605555556 "
+ "+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 "
+ "+y_0=463000 +ellps=bessel");
+}
+
+// ---------------------------------------------------------------------------
+
static VerticalCRSNNPtr createVerticalCRS() {
PropertyMap propertiesVDatum;
propertiesVDatum.set(Identifier::CODESPACE_KEY, "EPSG")
@@ -5941,8 +5970,8 @@ static BoundCRSNNPtr createBoundVerticalCRS() {
auto vertCRS = createVerticalCRS();
auto transformation =
Transformation::createGravityRelatedHeightToGeographic3D(
- PropertyMap(), vertCRS, GeographicCRS::EPSG_4979, "egm08_25.gtx",
- std::vector<PositionalAccuracyNNPtr>());
+ PropertyMap(), vertCRS, GeographicCRS::EPSG_4979, nullptr,
+ "egm08_25.gtx", std::vector<PositionalAccuracyNNPtr>());
return BoundCRS::create(vertCRS, GeographicCRS::EPSG_4979, transformation);
}
@@ -6727,6 +6756,22 @@ TEST(operation, compoundCRS_from_WKT2_no_id_to_geogCRS_3D_context) {
auto list =
CoordinateOperationFactory::create()->createOperations(src, dst, ctxt);
ASSERT_GE(list.size(), 1U);
+
+ {
+ // Important here is vgridshift before hgridshift
+ auto op_proj =
+ list[0]->exportToPROJString(PROJStringFormatter::create().get());
+ EXPECT_EQ(
+ op_proj,
+ "+proj=pipeline +step +inv +proj=sterea +lat_0=52.1561605555556 "
+ "+lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 "
+ "+ellps=bessel "
+ "+step +proj=vgridshift +grids=naptrans2008.gtx +multiplier=1 "
+ "+step +proj=hgridshift +grids=rdtrans2008.gsb "
+ "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m "
+ "+step +proj=axisswap +order=2,1");
+ }
+
auto wkt2 =
"COMPOUNDCRS[\"unknown\",\n"
" PROJCRS[\"unknown\",\n"
@@ -6759,8 +6804,12 @@ TEST(operation, compoundCRS_from_WKT2_no_id_to_geogCRS_3D_context) {
for (size_t i = 0; i < list.size(); i++) {
const auto &op = list[i];
const auto &op2 = list2[i];
- EXPECT_TRUE(
- op->isEquivalentTo(op2.get(), IComparable::Criterion::EQUIVALENT));
+ auto op_proj =
+ op->exportToPROJString(PROJStringFormatter::create().get());
+ auto op2_proj =
+ op2->exportToPROJString(PROJStringFormatter::create().get());
+ EXPECT_EQ(op_proj, op2_proj) << "op=" << op->nameStr()
+ << " op2=" << op2->nameStr();
}
}
diff --git a/travis/osx/before_install.sh b/travis/osx/before_install.sh
index a5d40af5..964fbc67 100755
--- a/travis/osx/before_install.sh
+++ b/travis/osx/before_install.sh
@@ -8,6 +8,7 @@ brew update
brew install ccache
brew install sqlite3
brew install doxygen
+brew install md5sha1sum
export PATH=$HOME/Library/Python/2.7/bin:$PATH
# breathe=4.12.0 is the last version to work for us with sphinx 1.8.5 / Python 2