Skip to content

Commit ef9aadb

Browse files
committed
Expose extra geometries as WKT
1 parent c6e53de commit ef9aadb

2 files changed

Lines changed: 94 additions & 4 deletions

File tree

src/server/services/wfs3/qgswfs3handlers.cpp

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "qgslogger.h"
3232
#include "qgsmessagelog.h"
3333
#include "qgsogrutils.h"
34+
#include "qgsreferencedgeometry.h"
3435
#include "qgsserverapicontext.h"
3536
#include "qgsserverapiutils.h"
3637
#include "qgsserverfeatureid.h"
@@ -455,8 +456,26 @@ void QgsWfs3AbstractItemsHandler::gatherLayerFieldsInfo( json &data, const QgsVe
455456
fInfo.type = "string";
456457
break;
457458
}
459+
case QMetaType::Type::QByteArray:
460+
{
461+
fInfo.type = "binary";
462+
break;
463+
}
464+
case QMetaType::Type::User:
465+
{
466+
if ( field.typeName().toLower() == "geometry" )
467+
{
468+
// no format: we do not have the information about the geometry type
469+
fInfo.format = "geometry-any";
470+
fInfo.type = "string";
471+
}
472+
break;
473+
}
458474
default:
475+
{
476+
QgsDebugMsgLevel( u"Field '%1' has unsupported meta type '%2', defaulting to string"_s.arg( field.name() ).arg( field.type() ), 2 );
459477
break;
478+
}
460479
}
461480
};
462481

@@ -667,6 +686,8 @@ void QgsWfs3AbstractItemsHandler::gatherLayerFieldsInfo( json &data, const QgsVe
667686
// For now, we skip and warn (info level) assuming that if the widget is not set this is intentional.
668687
QgsMessageLog::logMessage( u"Unhandled widget type '%1' for field '%2'"_s.arg( widgetType, fieldIdentifier ), u"Server"_s, Qgis::MessageLevel::Info );
669688
}
689+
// ////////////////////////////////////////////////////
690+
// Generic code that may apply to several widget types
670691

671692
// Extract the pattern
672693
const QgsFieldConstraints constraints = field.constraints();
@@ -692,9 +713,6 @@ void QgsWfs3AbstractItemsHandler::gatherLayerFieldsInfo( json &data, const QgsVe
692713
}
693714
}
694715

695-
// ////////////////////////////////////////////////////
696-
// Generic code that may apply to several widget types
697-
698716
// Check for std::numeric_limits max/min(lowest for double) and reset if found
699717
if ( fInfo.maximum.has_value() )
700718
{
@@ -1772,7 +1790,20 @@ void QgsWfs3CollectionsItemsHandler::writeJsonOutput( const QgsVectorLayer *mapL
17721790

17731791
json data = exporter.exportFeaturesToJsonObject( featureList );
17741792

1775-
// Patch feature IDs with server feature IDs
1793+
QMap<int, QString> extraGeomFields;
1794+
const QList<int> requestedAttrs = featureRequest.subsetOfAttributes();
1795+
for ( const int idx : std::as_const( requestedAttrs ) )
1796+
{
1797+
if ( mapLayer->fields().field( idx ).type() == QMetaType::Type::User && mapLayer->fields().field( idx ).typeName().compare( "geometry"_L1 ) == 0 )
1798+
{
1799+
extraGeomFields.insert( idx, mapLayer->fields().field( idx ).displayName() );
1800+
}
1801+
}
1802+
1803+
// Patch feature
1804+
// - IDs with server feature IDs
1805+
// - add references
1806+
// - format extra geometries
17761807
for ( int i = 0; i < featureList.length(); i++ )
17771808
{
17781809
data["features"][i]["id"] = fidMap.value( data["features"][i]["id"] ).toStdString();
@@ -1786,6 +1817,11 @@ void QgsWfs3CollectionsItemsHandler::writeJsonOutput( const QgsVectorLayer *mapL
17861817
data["features"][i]["properties"][fieldIdentifier.toStdString()] = relatedFeatureReference( rawValue, referencedInfo, relAs, apiContext );
17871818
}
17881819
}
1820+
for ( const auto &[fieldIdx, fieldIdentifier] : extraGeomFields.toStdMap() )
1821+
{
1822+
const QgsReferencedGeometry &refGeom = featureList[i].attribute( fieldIdx ).value<QgsReferencedGeometry>();
1823+
data["features"][i]["properties"][fieldIdentifier.toStdString()] = refGeom.asWkt().toStdString();
1824+
}
17891825
}
17901826

17911827
// Add some metadata

tests/src/python/test_qgsserver_ogcapi_schema.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,60 @@ def test_relation(self):
563563
},
564564
)
565565

566+
def test_extra_geometry(self):
567+
568+
if "QGIS_PGTEST_DB" in os.environ:
569+
dbconn = os.environ["QGIS_PGTEST_DB"]
570+
else:
571+
dbconn = "service=qgis_test dbname=qgis_test sslmode=disable "
572+
573+
# Test layer
574+
uri = dbconn + " dbname=qgis_test sslmode=disable "
575+
md = QgsProviderRegistry.instance().providerMetadata("postgres")
576+
577+
if not md:
578+
self.skipTest("postgres provider not available")
579+
580+
conn = md.createConnection(uri, {})
581+
conn.executeSql('DROP TABLE IF EXISTS "qgis_test"."TwoGeoms" CASCADE')
582+
conn.executeSql('CREATE TABLE "qgis_test"."TwoGeoms" (id serial primary key)')
583+
584+
conn.executeSql(
585+
"SELECT AddGeometryColumn('qgis_test', 'TwoGeoms', 'geom1', 4326, 'POINT', 2 )"
586+
)
587+
conn.executeSql(
588+
"SELECT AddGeometryColumn('qgis_test', 'TwoGeoms', 'geom2', 4326, 'POINT', 2 )"
589+
)
590+
591+
conn.executeSql(
592+
"INSERT INTO \"qgis_test\".\"TwoGeoms\" (geom1, geom2) VALUES (ST_GeomFromText('POINT(0 0)', 4326), ST_GeomFromText('POINT(1 1)', 4326))"
593+
)
594+
595+
layer_source = conn.tableUri("qgis_test", "TwoGeoms") + " (geom2) sql="
596+
597+
project = QgsProject()
598+
layer = QgsVectorLayer(layer_source, "TwoGeoms", "postgres")
599+
self.assertTrue(layer.isValid())
600+
601+
project.addMapLayer(layer)
602+
# Expose to WFS
603+
project.writeEntry("WFSLayers", "/", [layer.id()])
604+
605+
j = self._getJsonResponse(
606+
"http://server.qgis.org/wfs3/collections/TwoGeoms/schema.json", project
607+
)
608+
609+
self.assertEqual(
610+
j["properties"]["geom1"],
611+
{"format": "geometry-any", "type": "string", "x-ogc-propertySeq": 1},
612+
)
613+
614+
# Get data and check geom1 is correctly exposed as WKT
615+
j = self._getJsonResponse(
616+
"http://server.qgis.org/wfs3/collections/TwoGeoms/items?f=json", project
617+
)
618+
self.assertEqual(j["features"][0]["properties"]["geom1"].upper(), "POINT (0 0)")
619+
566620

567621
if __name__ == "__main__":
568622
unittest.main()

0 commit comments

Comments
 (0)