aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@mines-paris.org>2019-03-26 12:07:27 +0100
committerGitHub <noreply@github.com>2019-03-26 12:07:27 +0100
commitd21fb4e9abd3246fcbece6724dd3d014c35df25e (patch)
treeee4ba388d7d9507397b5cbad5814cb7e1ee540c0
parent66774791d16d1b197911e595aaaceb9690c8ca14 (diff)
parent09db4826d4a1e5df900cb4b93a4b3eae2c487cb9 (diff)
downloadPROJ-d21fb4e9abd3246fcbece6724dd3d014c35df25e.tar.gz
PROJ-d21fb4e9abd3246fcbece6724dd3d014c35df25e.zip
Merge pull request #1366 from rouault/ogc_18_010r6_further_fixes
OGC 18-010r6 further fixes regarding WKT2:2018 export
-rw-r--r--include/proj/crs.hpp2
-rw-r--r--include/proj/io.hpp12
-rw-r--r--src/iso19111/common.cpp2
-rw-r--r--src/iso19111/coordinateoperation.cpp88
-rw-r--r--src/iso19111/crs.cpp17
-rw-r--r--src/iso19111/io.cpp40
-rw-r--r--test/cli/testprojinfo_out.dist61
-rw-r--r--test/unit/test_crs.cpp139
-rw-r--r--test/unit/test_factory.cpp18
-rw-r--r--test/unit/test_io.cpp44
-rw-r--r--test/unit/test_operation.cpp4
11 files changed, 333 insertions, 94 deletions
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index b9594165..ed3463cd 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -503,7 +503,7 @@ class PROJ_GCC_DLL DerivedCRS : virtual public SingleCRS {
PROJ_INTERNAL void setDerivingConversionCRS();
PROJ_INTERNAL void baseExportToWKT(
- io::WKTFormatter *&formatter, const std::string &keyword,
+ io::WKTFormatter *formatter, const std::string &keyword,
const std::string &baseKeyword) const; // throw(FormattingException)
PROJ_INTERNAL bool
diff --git a/include/proj/io.hpp b/include/proj/io.hpp
index 8cf8e3c4..5386ca6c 100644
--- a/include/proj/io.hpp
+++ b/include/proj/io.hpp
@@ -195,8 +195,7 @@ class PROJ_GCC_DLL WKTFormatter {
WKT2_2018_SIMPLIFIED,
/** WKT1 as traditionally output by GDAL, deriving from OGC 01-009.
- A notable departuPROJ_GCC_DLLre from WKT1_GDAL with respect to OGC
- 01-009 is
+ A notable departure from WKT1_GDAL with respect to OGC 01-009 is
that in WKT1_GDAL, the unit of the PRIMEM value is always degrees.
*/
WKT1_GDAL,
@@ -262,6 +261,13 @@ class PROJ_GCC_DLL WKTFormatter {
PROJ_INTERNAL void popOutputId();
PROJ_INTERNAL bool outputId() const;
+ PROJ_INTERNAL void pushHasId(bool hasId);
+ PROJ_INTERNAL void popHasId();
+
+ PROJ_INTERNAL void pushDisableUsage();
+ PROJ_INTERNAL void popDisableUsage();
+ PROJ_INTERNAL bool outputUsage() const;
+
PROJ_INTERNAL void
pushAxisLinearUnit(const common::UnitOfMeasureNNPtr &unit);
PROJ_INTERNAL void popAxisLinearUnit();
@@ -303,6 +309,8 @@ class PROJ_GCC_DLL WKTFormatter {
PROJ_INTERNAL bool primeMeridianOrParameterUnitOmittedIfSameAsAxis() const;
PROJ_INTERNAL bool primeMeridianInDegree() const;
PROJ_INTERNAL bool outputCSUnitOnlyOnceIfSame() const;
+ PROJ_INTERNAL bool idOnTopLevelOnly() const;
+ PROJ_INTERNAL bool topLevelHasId() const;
/** WKT version. */
enum class Version {
diff --git a/src/iso19111/common.cpp b/src/iso19111/common.cpp
index 4b947dc9..d46da0da 100644
--- a/src/iso19111/common.cpp
+++ b/src/iso19111/common.cpp
@@ -1073,7 +1073,7 @@ void ObjectUsage::setProperties(
void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const {
const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
- if (isWKT2 && formatter->outputId()) {
+ if (isWKT2 && formatter->outputUsage()) {
auto l_domains = domains();
if (!l_domains.empty()) {
if (formatter->use2018Keywords()) {
diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp
index 80c1a572..d7f138a4 100644
--- a/src/iso19111/coordinateoperation.cpp
+++ b/src/iso19111/coordinateoperation.cpp
@@ -7639,6 +7639,53 @@ void Transformation::_exportToWKT(io::WKTFormatter *formatter) const {
// ---------------------------------------------------------------------------
+static void exportSourceCRSAndTargetCRSToWKT(const CoordinateOperation *co,
+ io::WKTFormatter *formatter) {
+ auto l_sourceCRS = co->sourceCRS();
+ assert(l_sourceCRS);
+ auto l_targetCRS = co->targetCRS();
+ assert(l_targetCRS);
+ const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
+ const bool canExportCRSId =
+ (isWKT2 && formatter->use2018Keywords() &&
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId()));
+
+ const bool hasDomains = !co->domains().empty();
+ if (hasDomains) {
+ formatter->pushDisableUsage();
+ }
+
+ formatter->startNode(io::WKTConstants::SOURCECRS, false);
+ if (canExportCRSId && !l_sourceCRS->identifiers().empty()) {
+ // fake that top node has no id, so that the sourceCRS id is
+ // considered
+ formatter->pushHasId(false);
+ l_sourceCRS->_exportToWKT(formatter);
+ formatter->popHasId();
+ } else {
+ l_sourceCRS->_exportToWKT(formatter);
+ }
+ formatter->endNode();
+
+ formatter->startNode(io::WKTConstants::TARGETCRS, false);
+ if (canExportCRSId && !l_targetCRS->identifiers().empty()) {
+ // fake that top node has no id, so that the targetCRS id is
+ // considered
+ formatter->pushHasId(false);
+ l_targetCRS->_exportToWKT(formatter);
+ formatter->popHasId();
+ } else {
+ l_targetCRS->_exportToWKT(formatter);
+ }
+ formatter->endNode();
+
+ if (hasDomains) {
+ formatter->popDisableUsage();
+ }
+}
+
+// ---------------------------------------------------------------------------
+
void SingleOperation::exportTransformationToWKT(
io::WKTFormatter *formatter) const {
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
@@ -7647,11 +7694,6 @@ void SingleOperation::exportTransformationToWKT(
"Transformation can only be exported to WKT2");
}
- auto l_sourceCRS = sourceCRS();
- assert(l_sourceCRS);
- auto l_targetCRS = targetCRS();
- assert(l_targetCRS);
-
if (formatter->abridgedTransformation()) {
formatter->startNode(io::WKTConstants::ABRIDGEDTRANSFORMATION,
!identifiers().empty());
@@ -7672,13 +7714,7 @@ void SingleOperation::exportTransformationToWKT(
}
if (!formatter->abridgedTransformation()) {
- formatter->startNode(io::WKTConstants::SOURCECRS, false);
- l_sourceCRS->_exportToWKT(formatter);
- formatter->endNode();
-
- formatter->startNode(io::WKTConstants::TARGETCRS, false);
- l_targetCRS->_exportToWKT(formatter);
- formatter->endNode();
+ exportSourceCRSAndTargetCRSToWKT(this, formatter);
}
method()->_exportToWKT(formatter);
@@ -9337,20 +9373,34 @@ void ConcatenatedOperation::_exportToWKT(io::WKTFormatter *formatter) const {
}
}
- formatter->startNode(io::WKTConstants::SOURCECRS, false);
- sourceCRS()->_exportToWKT(formatter);
- formatter->endNode();
+ exportSourceCRSAndTargetCRSToWKT(this, formatter);
- formatter->startNode(io::WKTConstants::TARGETCRS, false);
- targetCRS()->_exportToWKT(formatter);
- formatter->endNode();
+ const bool canExportOperationId =
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId());
+
+ const bool hasDomains = !domains().empty();
+ if (hasDomains) {
+ formatter->pushDisableUsage();
+ }
for (const auto &operation : operations()) {
formatter->startNode(io::WKTConstants::STEP, false);
- operation->_exportToWKT(formatter);
+ if (canExportOperationId && !operation->identifiers().empty()) {
+ // fake that top node has no id, so that the operation id is
+ // considered
+ formatter->pushHasId(false);
+ operation->_exportToWKT(formatter);
+ formatter->popHasId();
+ } else {
+ operation->_exportToWKT(formatter);
+ }
formatter->endNode();
}
+ if (hasDomains) {
+ formatter->popDisableUsage();
+ }
+
ObjectUsage::baseExportToWKT(formatter);
formatter->endNode();
}
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index 9688883d..2dc6b3bf 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -2421,16 +2421,21 @@ void DerivedCRS::setDerivingConversionCRS() {
// ---------------------------------------------------------------------------
-void DerivedCRS::baseExportToWKT(io::WKTFormatter *&formatter,
+void DerivedCRS::baseExportToWKT(io::WKTFormatter *formatter,
const std::string &keyword,
const std::string &baseKeyword) const {
formatter->startNode(keyword, !identifiers().empty());
formatter->addQuotedString(nameStr());
const auto &l_baseCRS = d->baseCRS_;
- formatter->startNode(baseKeyword, !l_baseCRS->identifiers().empty());
+ formatter->startNode(baseKeyword, formatter->use2018Keywords() &&
+ !l_baseCRS->identifiers().empty());
formatter->addQuotedString(l_baseCRS->nameStr());
l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter);
+ if (formatter->use2018Keywords() &&
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) {
+ l_baseCRS->formatID(formatter);
+ }
formatter->endNode();
formatter->setUseDerivingConversion(true);
@@ -2658,7 +2663,7 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
dynamic_cast<const GeographicCRS *>(l_baseCRS.get()))
? io::WKTConstants::BASEGEOGCRS
: io::WKTConstants::BASEGEODCRS,
- !l_baseCRS->identifiers().empty());
+ formatter->use2018Keywords() && !l_baseCRS->identifiers().empty());
formatter->addQuotedString(l_baseCRS->nameStr());
l_baseCRS->exportDatumOrDatumEnsembleToWkt(formatter);
// insert ellipsoidal cs unit when the units of the map
@@ -2669,6 +2674,10 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
geodeticCRSAxisList[0]->unit()._exportToWKT(formatter);
}
l_baseCRS->primeMeridian()->_exportToWKT(formatter);
+ if (formatter->use2018Keywords() &&
+ !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId())) {
+ l_baseCRS->formatID(formatter);
+ }
formatter->endNode();
} else {
const auto oldAxisOutputRule = formatter->outputAxis();
@@ -4848,7 +4857,7 @@ DerivedCRSTemplate<DerivedCRSTraits>::create(
// ---------------------------------------------------------------------------
-static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *&formatter,
+static void DerivedCRSTemplateCheckExportToWKT(io::WKTFormatter *formatter,
const std::string &crsName,
bool wkt2_2018_only) {
const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 220ee967..578234b4 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -141,6 +141,7 @@ struct WKTFormatter::Private {
std::vector<bool> stackHasChild_{};
std::vector<bool> stackHasId_{false};
std::vector<bool> stackEmptyKeyword_{};
+ std::vector<bool> stackDisableUsage_{};
std::vector<bool> outputUnitStack_{true};
std::vector<bool> outputIdStack_{true};
std::vector<UnitOfMeasureNNPtr> axisLinearUnitStack_{
@@ -272,6 +273,11 @@ const std::string &WKTFormatter::toString() const {
if (d->outputUnitStack_.size() != 1)
throw FormattingException(
"Unbalanced pushOutputUnit() / popOutputUnit()");
+ if (d->stackHasId_.size() != 1)
+ throw FormattingException("Unbalanced pushHasId() / popHasId()");
+ if (!d->stackDisableUsage_.empty())
+ throw FormattingException(
+ "Unbalanced pushDisableUsage() / popDisableUsage()");
return d->result_;
}
@@ -556,6 +562,28 @@ bool WKTFormatter::outputId() const {
// ---------------------------------------------------------------------------
+void WKTFormatter::pushHasId(bool hasId) { d->stackHasId_.push_back(hasId); }
+
+// ---------------------------------------------------------------------------
+
+void WKTFormatter::popHasId() { d->stackHasId_.pop_back(); }
+
+// ---------------------------------------------------------------------------
+
+void WKTFormatter::pushDisableUsage() { d->stackDisableUsage_.push_back(true); }
+
+// ---------------------------------------------------------------------------
+
+void WKTFormatter::popDisableUsage() { d->stackDisableUsage_.pop_back(); }
+
+// ---------------------------------------------------------------------------
+
+bool WKTFormatter::outputUsage() const {
+ return outputId() && d->stackDisableUsage_.empty();
+}
+
+// ---------------------------------------------------------------------------
+
void WKTFormatter::pushAxisLinearUnit(const UnitOfMeasureNNPtr &unit) {
d->axisLinearUnitStack_.push_back(unit);
}
@@ -634,6 +662,18 @@ bool WKTFormatter::primeMeridianInDegree() const {
// ---------------------------------------------------------------------------
+bool WKTFormatter::idOnTopLevelOnly() const {
+ return d->params_.idOnTopLevelOnly_;
+}
+
+// ---------------------------------------------------------------------------
+
+bool WKTFormatter::topLevelHasId() const {
+ return d->stackHasId_.size() >= 2 && d->stackHasId_[1];
+}
+
+// ---------------------------------------------------------------------------
+
WKTFormatter::Version WKTFormatter::version() const {
return d->params_.version_;
}
diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist
index a2e2cab9..eaa10689 100644
--- a/test/cli/testprojinfo_out.dist
+++ b/test/cli/testprojinfo_out.dist
@@ -193,10 +193,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
- USAGE[
- SCOPE["unknown"],
- AREA["North America - NAD27"],
- BBOX[7.15,167.65,83.17,-47.74]],
ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
@@ -212,10 +208,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
- USAGE[
- SCOPE["unknown"],
- AREA["North America - NAD83"],
- BBOX[14.92,167.65,86.46,-47.74]],
ID["EPSG",4269]]],
METHOD["Geographic2D offsets",
ID["EPSG",9619]],
@@ -265,7 +257,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (3)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -279,7 +272,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (3)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["NTv1",
ID["EPSG",9614]],
PARAMETERFILE["Latitude and longitude difference file","ntv1_can.dat"],
@@ -313,7 +307,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (4)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -327,7 +322,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (4)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["NTv2",
ID["EPSG",9615]],
PARAMETERFILE["Latitude and longitude difference file","ntv2_0.gsb"],
@@ -361,7 +357,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (1)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -375,7 +372,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (1)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["CTABLE2"],
PARAMETERFILE["Latitude and longitude difference file","conus"],
OPERATIONACCURACY[0.15],
@@ -408,7 +406,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -422,7 +421,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["CTABLE2"],
PARAMETERFILE["Latitude and longitude difference file","alaska"],
OPERATIONACCURACY[0.5],
@@ -455,7 +455,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -469,7 +470,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["NTv1",
ID["EPSG",9614]],
PARAMETERFILE["Latitude and longitude difference file","GS2783v1.QUE"],
@@ -503,7 +505,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (6)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
DATUM["North American Datum 1983",
@@ -517,7 +520,8 @@ COORDINATEOPERATION["NAD27 to NAD83 (6)",
ANGLEUNIT["degree",0.0174532925199433]],
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
- ANGLEUNIT["degree",0.0174532925199433]]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4269]]],
METHOD["NTv2",
ID["EPSG",9615]],
PARAMETERFILE["Latitude and longitude difference file","QUE27-83.gsb"],
@@ -552,10 +556,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
- USAGE[
- SCOPE["unknown"],
- AREA["North America - NAD27"],
- BBOX[7.15,167.65,83.17,-47.74]],
ID["EPSG",4267]]],
TARGETCRS[
GEOGCRS["NAD83",
@@ -571,10 +571,6 @@ COORDINATEOPERATION["Ballpark geographic offset from NAD27 to NAD83",
AXIS["geodetic longitude (Lon)",east,
ORDER[2],
ANGLEUNIT["degree",0.0174532925199433]],
- USAGE[
- SCOPE["unknown"],
- AREA["North America - NAD83"],
- BBOX[14.92,167.65,86.46,-47.74]],
ID["EPSG",4269]]],
METHOD["Geographic2D offsets",
ID["EPSG",9619]],
@@ -633,7 +629,8 @@ PROJCRS["Monte Mario (Rome) / Italy zone 1",
ELLIPSOID["International 1924",6378388,297,
LENGTHUNIT["metre",1]]],
PRIMEM["Rome",12.4523333333333,
- ANGLEUNIT["degree",0.0174532925199433]]],
+ ANGLEUNIT["degree",0.0174532925199433]],
+ ID["EPSG",4806]],
CONVERSION["Italy zone 1",
METHOD["Transverse Mercator",
ID["EPSG",9807]],
@@ -711,7 +708,8 @@ COORDINATEOPERATION["RH2000 height to SWEREF99",
VDATUM["Rikets hojdsystem 2000"],
CS[vertical,1],
AXIS["gravity-related height (H)",up,
- LENGTHUNIT["metre",1]]]],
+ LENGTHUNIT["metre",1]],
+ ID["EPSG",5613]]],
TARGETCRS[
GEOGCRS["SWEREF99 (3D)",
DATUM["SWEREF99",
@@ -728,7 +726,8 @@ COORDINATEOPERATION["RH2000 height to SWEREF99",
ANGLEUNIT["degree minute second hemisphere",0.0174532925199433]],
AXIS["ellipsoidal height (h)",up,
ORDER[3],
- LENGTHUNIT["metre",1]]]],
+ LENGTHUNIT["metre",1]],
+ ID["EPSG",4377]]],
METHOD["GravityRelatedHeight to Geographic3D",
ID["PROJ","HEIGHT_TO_GEOGRAPHIC3D"]],
PARAMETERFILE["Geoid (height correction) model file","SWEN17_RH2000.gtx"],
diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp
index 4953529c..d7303df9 100644
--- a/test/unit/test_crs.cpp
+++ b/test/unit/test_crs.cpp
@@ -148,32 +148,72 @@ TEST(crs, GeographicCRS_datum_ensemble) {
std::vector<DatumNNPtr>{GeodeticReferenceFrame::EPSG_6326,
GeodeticReferenceFrame::EPSG_6326},
PositionalAccuracy::create("100"));
- auto crs = GeographicCRS::create(
- PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr,
- ensemble_vdatum,
- EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE));
- WKTFormatterNNPtr f(
- WKTFormatter::create(WKTFormatter::Convention::WKT2_2018));
- f->simulCurNodeHasId();
- crs->exportToWKT(f.get());
- auto expected = "GEOGCRS[\"unnamed\",\n"
- " ENSEMBLE[\"unnamed\",\n"
- " MEMBER[\"World Geodetic System 1984\"],\n"
- " MEMBER[\"World Geodetic System 1984\"],\n"
- " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
- " LENGTHUNIT[\"metre\",1]],\n"
- " ENSEMBLEACCURACY[100]],\n"
- " PRIMEM[\"Greenwich\",0,\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
- " CS[ellipsoidal,2],\n"
- " AXIS[\"latitude\",north,\n"
- " ORDER[1],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
- " AXIS[\"longitude\",east,\n"
- " ORDER[2],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]]]";
+ {
+ auto crs = GeographicCRS::create(
+ PropertyMap()
+ .set(IdentifiedObject::NAME_KEY, "unnamed")
+ .set(Identifier::CODESPACE_KEY, "MY_CODESPACE")
+ .set(Identifier::CODE_KEY, "MY_ID"),
+ nullptr, ensemble_vdatum,
+ EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE));
+ WKTFormatterNNPtr f(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018));
+ crs->exportToWKT(f.get());
+ auto expected =
+ "GEOGCRS[\"unnamed\",\n"
+ " ENSEMBLE[\"unnamed\",\n"
+ " MEMBER[\"World Geodetic System 1984\"],\n"
+ " MEMBER[\"World Geodetic System 1984\"],\n"
+ " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " ENSEMBLEACCURACY[100]],\n"
+ " PRIMEM[\"Greenwich\",0,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " CS[ellipsoidal,2],\n"
+ " AXIS[\"latitude\",north,\n"
+ " ORDER[1],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " AXIS[\"longitude\",east,\n"
+ " ORDER[2],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"MY_CODESPACE\",\"MY_ID\"]]";
- EXPECT_EQ(f->toString(), expected);
+ EXPECT_EQ(f->toString(), expected);
+ }
+
+ {
+ auto crs = GeographicCRS::create(
+ PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), nullptr,
+ ensemble_vdatum,
+ EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE));
+ WKTFormatterNNPtr f(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018));
+ crs->exportToWKT(f.get());
+ auto expected = "GEOGCRS[\"unnamed\",\n"
+ " ENSEMBLE[\"unnamed\",\n"
+ " MEMBER[\"World Geodetic System 1984\",\n"
+ " ID[\"EPSG\",6326]],\n"
+ " MEMBER[\"World Geodetic System 1984\",\n"
+ " ID[\"EPSG\",6326]],\n"
+ " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
+ " LENGTHUNIT[\"metre\",1],\n"
+ " ID[\"EPSG\",7030]],\n"
+ " ENSEMBLEACCURACY[100]],\n"
+ " PRIMEM[\"Greenwich\",0,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433],\n"
+ " ID[\"EPSG\",8901]],\n"
+ " CS[ellipsoidal,2],\n"
+ " AXIS[\"latitude\",north,\n"
+ " ORDER[1],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433,\n"
+ " ID[\"EPSG\",9122]]],\n"
+ " AXIS[\"longitude\",east,\n"
+ " ORDER[2],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433,\n"
+ " ID[\"EPSG\",9122]]]]";
+
+ EXPECT_EQ(f->toString(), expected);
+ }
}
// ---------------------------------------------------------------------------
@@ -1561,6 +1601,52 @@ TEST(crs, projectedCRS_as_WKT2) {
// ---------------------------------------------------------------------------
+TEST(crs, projectedCRS_as_WKT2_2018) {
+ auto crs = createProjected();
+
+ auto expected =
+ "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
+ " BASEGEOGCRS[\"WGS 84\",\n"
+ " DATUM[\"World Geodetic System 1984\",\n"
+ " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
+ " LENGTHUNIT[\"metre\",1]]],\n"
+ " PRIMEM[\"Greenwich\",0,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"EPSG\",4326]],\n"
+ " CONVERSION[\"UTM zone 31N\",\n"
+ " METHOD[\"Transverse Mercator\",\n"
+ " ID[\"EPSG\",9807]],\n"
+ " PARAMETER[\"Latitude of natural origin\",0,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433],\n"
+ " ID[\"EPSG\",8801]],\n"
+ " PARAMETER[\"Longitude of natural origin\",3,\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433],\n"
+ " ID[\"EPSG\",8802]],\n"
+ " PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
+ " SCALEUNIT[\"unity\",1],\n"
+ " ID[\"EPSG\",8805]],\n"
+ " PARAMETER[\"False easting\",500000,\n"
+ " LENGTHUNIT[\"metre\",1],\n"
+ " ID[\"EPSG\",8806]],\n"
+ " PARAMETER[\"False northing\",0,\n"
+ " LENGTHUNIT[\"metre\",1],\n"
+ " ID[\"EPSG\",8807]]],\n"
+ " CS[Cartesian,2],\n"
+ " AXIS[\"(E)\",east,\n"
+ " ORDER[1],\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " AXIS[\"(N)\",north,\n"
+ " ORDER[2],\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " ID[\"EPSG\",32631]]";
+
+ EXPECT_EQ(
+ crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get()),
+ expected);
+}
+// ---------------------------------------------------------------------------
+
TEST(crs, projectedCRS_as_WKT2_simplified) {
auto crs = createProjected();
@@ -4522,7 +4608,8 @@ TEST(crs, DerivedVerticalCRS_WKT2) {
auto expected = "VERTCRS[\"Derived vertCRS\",\n"
" BASEVERTCRS[\"ODN height\",\n"
- " VDATUM[\"Ordnance Datum Newlyn\"]],\n"
+ " VDATUM[\"Ordnance Datum Newlyn\",\n"
+ " ID[\"EPSG\",5101]]],\n"
" DERIVINGCONVERSION[\"unnamed\",\n"
" METHOD[\"PROJ unimplemented\"]],\n"
" CS[vertical,1],\n"
diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp
index 944e0ebe..f4c560d5 100644
--- a/test/unit/test_factory.cpp
+++ b/test/unit/test_factory.cpp
@@ -734,7 +734,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) {
" LENGTHUNIT[\"metre\",1]],\n"
" AXIS[\"(Z)\",geocentricZ,\n"
" ORDER[3],\n"
- " LENGTHUNIT[\"metre\",1]]]],\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " ID[\"EPSG\",5332]]],\n"
" TARGETCRS[\n"
" GEODCRS[\"GDA94\",\n"
" DATUM[\"Geocentric Datum of Australia 1994\",\n"
@@ -751,7 +752,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_helmert_15_CF) {
" LENGTHUNIT[\"metre\",1]],\n"
" AXIS[\"(Z)\",geocentricZ,\n"
" ORDER[3],\n"
- " LENGTHUNIT[\"metre\",1]]]],\n"
+ " LENGTHUNIT[\"metre\",1]],\n"
+ " ID[\"EPSG\",4938]]],\n"
" METHOD[\"Time-dependent Coordinate Frame rotation (geocen)\",\n"
" ID[\"EPSG\",1056]],\n"
" PARAMETER[\"X-axis translation\",-84.68,\n"
@@ -885,7 +887,8 @@ TEST(
" ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
" AXIS[\"geodetic longitude (Lon)\",east,\n"
" ORDER[2],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"EPSG\",4749]]],\n"
" TARGETCRS[\n"
" GEOGCRS[\"NEA74 Noumea\",\n"
" DATUM[\"NEA74 Noumea\",\n"
@@ -899,7 +902,8 @@ TEST(
" ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
" AXIS[\"geodetic longitude (Lon)\",east,\n"
" ORDER[2],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"EPSG\",4644]]],\n"
" METHOD[\"NTv2\",\n"
" ID[\"EPSG\",9615]],\n"
" PARAMETERFILE[\"Latitude and longitude difference "
@@ -953,7 +957,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) {
" ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
" AXIS[\"geodetic longitude (Lon)\",east,\n"
" ORDER[2],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"EPSG\",4818]]],\n"
" TARGETCRS[\n"
" GEOGCRS[\"S-JTSK\",\n"
" DATUM[\"System of the Unified Trigonometrical Cadastral "
@@ -968,7 +973,8 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_other_transformation) {
" ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
" AXIS[\"geodetic longitude (Lon)\",east,\n"
" ORDER[2],\n"
- " ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
+ " ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
+ " ID[\"EPSG\",4156]]],\n"
" METHOD[\"Longitude rotation\",\n"
" ID[\"EPSG\",9601]],\n"
" PARAMETER[\"Longitude offset\",-17.6666666666667,\n"
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 1d097d9b..17b3c334 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -1663,6 +1663,50 @@ TEST(wkt_parse, wkt2_2018_projected_with_id_in_basegeodcrs) {
ASSERT_TRUE(crs != nullptr);
ASSERT_EQ(crs->baseCRS()->identifiers().size(), 1U);
EXPECT_EQ(crs->baseCRS()->identifiers().front()->code(), "4326");
+
+ {
+ auto got_wkt = crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
+ EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos)
+ << got_wkt;
+ }
+
+ {
+ auto got_wkt = crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018_SIMPLIFIED)
+ .get());
+ EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") == std::string::npos)
+ << got_wkt;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(wkt_parse, wkt2_2018_projected_no_id_but_id_in_basegeodcrs) {
+ auto wkt = "PROJCRS[\"WGS 84 / UTM zone 31N\",\n"
+ " BASEGEOGCRS[\"WGS 84\",\n"
+ " DATUM[\"World Geodetic System 1984\",\n"
+ " ELLIPSOID[\"WGS 84\",6378137,298.257223563]],\n"
+ " ID[\"EPSG\",4326]],\n"
+ " CONVERSION[\"UTM zone 31N\",\n"
+ " METHOD[\"Transverse Mercator\"],\n"
+ " PARAMETER[\"Latitude of natural origin\",0],\n"
+ " PARAMETER[\"Longitude of natural origin\",3],\n"
+ " PARAMETER[\"Scale factor at natural origin\",0.9996],\n"
+ " PARAMETER[\"False easting\",500000],\n"
+ " PARAMETER[\"False northing\",0]],\n"
+ " CS[Cartesian,2],\n"
+ " AXIS[\"(E)\",east],\n"
+ " AXIS[\"(N)\",north],\n"
+ " UNIT[\"metre\",1]]";
+ auto obj = WKTParser().createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+
+ auto got_wkt = crs->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2018).get());
+ EXPECT_TRUE(got_wkt.find("ID[\"EPSG\",4326]]") != std::string::npos)
+ << got_wkt;
}
// ---------------------------------------------------------------------------
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index 9111b862..848fb23b 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -442,7 +442,6 @@ TEST(operation, concatenated_operation) {
{
auto formatter =
WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
- formatter->setOutputId(false);
src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get());
}
@@ -450,7 +449,6 @@ TEST(operation, concatenated_operation) {
{
auto formatter =
WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
- formatter->setOutputId(false);
dst_wkt = GeographicCRS::EPSG_4979->exportToWKT(formatter.get());
}
@@ -458,7 +456,6 @@ TEST(operation, concatenated_operation) {
{
auto formatter =
WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
- formatter->setOutputId(false);
step1_wkt = transf_1->exportToWKT(formatter.get());
}
@@ -466,7 +463,6 @@ TEST(operation, concatenated_operation) {
{
auto formatter =
WKTFormatter::create(WKTFormatter::Convention::WKT2_2018);
- formatter->setOutputId(false);
step2_wkt = transf_2->exportToWKT(formatter.get());
}