@@ -93,6 +93,7 @@ struct CRS::Private {
93
93
BoundCRSPtr canonicalBoundCRS_{};
94
94
std::string extensionProj4_{};
95
95
bool implicitCS_ = false ;
96
+ bool allowNonConformantWKT1Export_ = false ;
96
97
97
98
void setImplicitCS (const util::PropertyMap &properties) {
98
99
const auto pVal = properties.get (" IMPLICIT_CS" );
@@ -559,6 +560,18 @@ CRSNNPtr CRS::shallowClone() const { return _shallowClone(); }
559
560
560
561
// ! @cond Doxygen_Suppress
561
562
563
+ CRSNNPtr CRS::allowNonConformantWKT1Export () const {
564
+ auto crs = shallowClone ();
565
+ crs->d ->allowNonConformantWKT1Export_ = true ;
566
+ return crs;
567
+ }
568
+
569
+ // ! @endcond
570
+
571
+ // ---------------------------------------------------------------------------
572
+
573
+ // ! @cond Doxygen_Suppress
574
+
562
575
CRSNNPtr CRS::alterName (const std::string &newName) const {
563
576
auto crs = shallowClone ();
564
577
auto newNameMod (newName);
@@ -1334,30 +1347,49 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
1334
1347
const bool isWKT2 = formatter->version () == io::WKTFormatter::Version::WKT2;
1335
1348
const bool isGeographic =
1336
1349
dynamic_cast <const GeographicCRS *>(this ) != nullptr ;
1337
- formatter->startNode (isWKT2
1338
- ? ((formatter->use2019Keywords () && isGeographic)
1339
- ? io::WKTConstants::GEOGCRS
1340
- : io::WKTConstants::GEODCRS)
1341
- : isGeocentric () ? io::WKTConstants::GEOCCS
1342
- : io::WKTConstants::GEOGCS,
1343
- !identifiers ().empty ());
1344
- auto l_name = nameStr ();
1350
+
1345
1351
const auto &cs = coordinateSystem ();
1346
1352
const auto &axisList = cs->axisList ();
1347
-
1348
1353
const auto oldAxisOutputRule = formatter->outputAxis ();
1354
+ auto l_name = nameStr ();
1355
+ const auto &dbContext = formatter->databaseContext ();
1349
1356
1350
1357
if (formatter->useESRIDialect ()) {
1351
1358
if (axisList.size () != 2 ) {
1352
1359
io::FormattingException::Throw (
1353
1360
" Only export of Geographic 2D CRS is supported in WKT1_ESRI" );
1354
1361
}
1362
+ }
1363
+
1364
+ if (!isWKT2 && formatter->isStrict () && isGeographic &&
1365
+ axisList.size () != 2 &&
1366
+ oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) {
1367
+ if (CRS::getPrivate ()->allowNonConformantWKT1Export_ ) {
1368
+ formatter->startNode (io::WKTConstants::COMPD_CS, false );
1369
+ formatter->addQuotedString (l_name + " + " + l_name);
1370
+ auto geogCRS = demoteTo2D (std::string (), dbContext);
1371
+ geogCRS->_exportToWKT (formatter);
1372
+ geogCRS->_exportToWKT (formatter);
1373
+ formatter->endNode ();
1374
+ return ;
1375
+ }
1376
+ io::FormattingException::Throw (
1377
+ " WKT1 does not support Geographic 3D CRS." );
1378
+ }
1355
1379
1380
+ formatter->startNode (isWKT2
1381
+ ? ((formatter->use2019Keywords () && isGeographic)
1382
+ ? io::WKTConstants::GEOGCRS
1383
+ : io::WKTConstants::GEODCRS)
1384
+ : isGeocentric () ? io::WKTConstants::GEOCCS
1385
+ : io::WKTConstants::GEOGCS,
1386
+ !identifiers ().empty ());
1387
+
1388
+ if (formatter->useESRIDialect ()) {
1356
1389
if (l_name == " WGS 84" ) {
1357
1390
l_name = " GCS_WGS_1984" ;
1358
1391
} else {
1359
1392
bool aliasFound = false ;
1360
- const auto &dbContext = formatter->databaseContext ();
1361
1393
if (dbContext) {
1362
1394
auto l_alias = dbContext->getAliasFromOfficialName (
1363
1395
l_name, " geodetic_crs" , " ESRI" );
@@ -1373,11 +1405,6 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
1373
1405
}
1374
1406
}
1375
1407
}
1376
- } else if (!isWKT2 && formatter->isStrict () && isGeographic &&
1377
- axisList.size () != 2 &&
1378
- oldAxisOutputRule != io::WKTFormatter::OutputAxisRule::NO) {
1379
- io::FormattingException::Throw (
1380
- " WKT1 does not support Geographic 3D CRS." );
1381
1408
}
1382
1409
1383
1410
if (!isWKT2 && !formatter->useESRIDialect () && isDeprecated ()) {
@@ -3146,6 +3173,36 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
3146
3173
const auto &dbContext = formatter->databaseContext ();
3147
3174
3148
3175
auto l_name = nameStr ();
3176
+ const auto &l_coordinateSystem = d->coordinateSystem ();
3177
+ const auto &axisList = l_coordinateSystem->axisList ();
3178
+ if (axisList.size () == 3 && !(isWKT2 && formatter->use2019Keywords ())) {
3179
+ if (!formatter->useESRIDialect () &&
3180
+ CRS::getPrivate ()->allowNonConformantWKT1Export_ ) {
3181
+ formatter->startNode (io::WKTConstants::COMPD_CS, false );
3182
+ formatter->addQuotedString (l_name + " + " + baseCRS ()->nameStr ());
3183
+ auto projCRS2D = demoteTo2D (std::string (), dbContext);
3184
+ if (dbContext) {
3185
+ const auto res =
3186
+ projCRS2D->identify (io::AuthorityFactory::create (
3187
+ NN_NO_CHECK (dbContext), " EPSG" ));
3188
+ if (res.size () == 1 ) {
3189
+ const auto &front = res.front ();
3190
+ if (front.second == 100 ) {
3191
+ projCRS2D = front.first ;
3192
+ }
3193
+ }
3194
+ }
3195
+ projCRS2D->_exportToWKT (formatter);
3196
+ baseCRS ()
3197
+ ->demoteTo2D (std::string (), dbContext)
3198
+ ->_exportToWKT (formatter);
3199
+ formatter->endNode ();
3200
+ return ;
3201
+ }
3202
+ io::FormattingException::Throw (
3203
+ " Projected 3D CRS can only be exported since WKT2:2019" );
3204
+ }
3205
+
3149
3206
std::string l_alias;
3150
3207
if (formatter->useESRIDialect () && dbContext) {
3151
3208
l_alias = dbContext->getAliasFromOfficialName (l_name, " projected_crs" ,
@@ -3197,13 +3254,6 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
3197
3254
}
3198
3255
}
3199
3256
3200
- const auto &l_coordinateSystem = d->coordinateSystem ();
3201
- const auto &axisList = l_coordinateSystem->axisList ();
3202
- if (axisList.size () == 3 && !(isWKT2 && formatter->use2019Keywords ())) {
3203
- io::FormattingException::Throw (
3204
- " Projected 3D CRS can only be exported since WKT2:2019" );
3205
- }
3206
-
3207
3257
const auto exportAxis = [&l_coordinateSystem, &axisList, &formatter]() {
3208
3258
const auto oldAxisOutputRule = formatter->outputAxis ();
3209
3259
if (oldAxisOutputRule ==
@@ -4073,6 +4123,54 @@ CompoundCRSNNPtr CompoundCRS::create(const util::PropertyMap &properties,
4073
4123
4074
4124
// ---------------------------------------------------------------------------
4075
4125
4126
+ // ! @cond Doxygen_Suppress
4127
+
4128
+ /* * \brief Instantiate a CompoundCRS, a Geographic 3D CRS or a Projected CRS
4129
+ * from a vector of CRS.
4130
+ *
4131
+ * Be a bit "lax", in allowing formulations like EPSG:4326+4326 or
4132
+ * EPSG:32631+4326 to express Geographic 3D CRS / Projected3D CRS.
4133
+ *
4134
+ * @param properties See \ref general_properties.
4135
+ * At minimum the name should be defined.
4136
+ * @param components the component CRS of the CompoundCRS.
4137
+ * @return new CRS.
4138
+ * @throw InvalidCompoundCRSException
4139
+ */
4140
+ CRSNNPtr CompoundCRS::createLax (const util::PropertyMap &properties,
4141
+ const std::vector<CRSNNPtr> &components,
4142
+ const io::DatabaseContextPtr &dbContext) {
4143
+
4144
+ if (components.size () == 2 ) {
4145
+ auto comp0 = components[0 ].get ();
4146
+ auto comp1 = components[1 ].get ();
4147
+ auto comp0Geog = dynamic_cast <const GeographicCRS *>(comp0);
4148
+ auto comp0Proj = dynamic_cast <const ProjectedCRS *>(comp0);
4149
+ auto comp1Geog = dynamic_cast <const GeographicCRS *>(comp1);
4150
+ if ((comp0Geog != nullptr || comp0Proj != nullptr ) &&
4151
+ comp1Geog != nullptr ) {
4152
+ const auto horizGeog =
4153
+ (comp0Proj != nullptr )
4154
+ ? comp0Proj->baseCRS ().as_nullable ().get ()
4155
+ : comp0Geog;
4156
+ if (horizGeog->_isEquivalentTo (
4157
+ comp1Geog->demoteTo2D (std::string (), dbContext).get ())) {
4158
+ return components[0 ]
4159
+ ->promoteTo3D (std::string (), dbContext)
4160
+ ->allowNonConformantWKT1Export ();
4161
+ }
4162
+ throw InvalidCompoundCRSException (
4163
+ " The 'vertical' geographic CRS is not equivalent to the "
4164
+ " geographic CRS of the horizontal part" );
4165
+ }
4166
+ }
4167
+
4168
+ return create (properties, components);
4169
+ }
4170
+ // ! @endcond
4171
+
4172
+ // ---------------------------------------------------------------------------
4173
+
4076
4174
// ! @cond Doxygen_Suppress
4077
4175
void CompoundCRS::_exportToWKT (io::WKTFormatter *formatter) const {
4078
4176
const bool isWKT2 = formatter->version () == io::WKTFormatter::Version::WKT2;
0 commit comments