Browse a WFS service for layers and add them to the map.

Use case
Services often have multiple layers available for display. For example, a feature service for a city might have layers representing roads, land masses, building footprints, parks, and facilities. A user can choose to only show the road network and parks for a park accessibility analysis.
How to use the sample
A list of layers in the WFS service will be shown. Select a layer to display.
Some WFS services return coordinates in X,Y order, while others return coordinates in lat/long (Y,X) order. If you don’t see features rendered or you see features in the wrong location, click on “Swap Coordinate Order”.
How it works
- Create a
WfsServiceobject with a URL to a WFS feature service. - Obtain a list of
WfsLayerInfofromWfsService::ServiceInfo. - When a layer is selected, create a
WfsFeatureTablefrom theWfsLayerInfo.- Set the axis order if necessary.
- Create a feature layer from the feature table.
- Add the feature layer to the map.
Relevant API
- FeatureLayer
- WfsFeatureTable
- WfsFeatureTable::axisOrder
- WfsLayerInfo
- WfsService
- WfsServiceInfo
About the data
The sample is configured with a sample WFS service, but you can load other WFS services if desired. The default service shows Seattle downtown features hosted on ArcGIS Online.
Tags
browse, catalog, feature, layers, OGC, service, web, WFS
Sample Code
// [WriteFile Name=BrowseWfsLayers, Category=Layers]// [Legal]// Copyright 2019 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "BrowseWfsLayers.h"
// ArcGIS Maps SDK headers#include "CoreTypes.h"#include "Envelope.h"#include "Error.h"#include "FeatureLayer.h"#include "FeatureQueryResult.h"#include "GeodatabaseTypes.h"#include "LayerListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "Polygon.h"#include "QueryParameters.h"#include "SimpleLineSymbol.h"#include "SimpleMarkerSymbol.h"#include "SimpleRenderer.h"#include "SymbolTypes.h"#include "WfsFeatureTable.h"#include "WfsLayerInfo.h"#include "WfsService.h"#include "WfsServiceInfo.h"
// Qt headers#include <QFuture>
using namespace Esri::ArcGISRuntime;
BrowseWfsLayers::BrowseWfsLayers(QObject* parent /* = nullptr */): QObject(parent), m_map(new Map(BasemapStyle::ArcGISTopographic, this)){ // create WFS Service m_wfsService = new WfsService(QUrl("https://dservices2.arcgis.com/ZQgQTuoyBrtmoGdP/arcgis/services/Seattle_Downtown_Features/WFSServer?service=wfs&request=getcapabilities"),this);
// once WFS Service is loaded, populate QStringList with layer names for ComboBox connect(m_wfsService, &WfsService::doneLoading, this, [this](const Error& e) { if (!e.isEmpty()) return;
m_wfsLayersInfoList = m_wfsService->serviceInfo().layerInfos(); for (const WfsLayerInfo& i : std::as_const(m_wfsLayersInfoList)) m_layerInfoTitleList.append(i.title());
emit layerInfoTitleListChanged(); }); m_wfsService->load();}
BrowseWfsLayers::~BrowseWfsLayers() = default;
void BrowseWfsLayers::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<BrowseWfsLayers>("Esri.Samples", 1, 0, "BrowseWfsLayersSample");}
MapQuickView* BrowseWfsLayers::mapView() const{ return m_mapView;}
// Set the view (created in QML)void BrowseWfsLayers::setMapView(MapQuickView* mapView){ if (!mapView || mapView == m_mapView) return;
m_mapView = mapView; m_mapView->setMap(m_map);
emit mapViewChanged();}
void BrowseWfsLayers::createWfsFeatureTable(int index, bool swap){ if (m_wfsLayersInfoList.isEmpty()) return;
// if swapAxisOrder button is selected swap the axis otherwise reset to default(OgcAxisOrder::NoSwap or false) if (swap) m_swapAxis = !m_swapAxis; else m_swapAxis = false;
// enable busy indicator m_loadingIndicator = true; emit isLoadingChanged();
// clear previous layer m_map->operationalLayers()->clear(); // create WFS Feature Table from selected layer m_wfsFeatureTable = new WfsFeatureTable(m_wfsLayersInfoList[index], this);
// Set feature request mode to manual - only manual is supported at v100.5. // In this mode, you must manually populate the table - panning and zooming // won't request features automatically. m_wfsFeatureTable->setFeatureRequestMode(FeatureRequestMode::ManualCache);
if (m_swapAxis) m_wfsFeatureTable->setAxisOrder(OgcAxisOrder::Swap); else m_wfsFeatureTable->setAxisOrder(OgcAxisOrder::NoSwap);
// once WFS Feature Table is loaded, populate the table and add the layer to the map connect(m_wfsFeatureTable, &WfsFeatureTable::doneLoading, this, [this](const Error& e) { if(!e.isEmpty()) return;
populateWfsFeatureTable(); }); m_wfsFeatureTable->load();}
void BrowseWfsLayers::populateWfsFeatureTable(){ if (!m_mapView || !m_wfsFeatureTable) return;
// create query parameters QueryParameters params; params.setGeometry(m_mapView->visibleArea().extent()); params.setSpatialRelationship(SpatialRelationship::Intersects);
// query the service constexpr bool clearCache = false; const QStringList outFields {"*"}; m_wfsFeatureTable->populateFromServiceAsync(params, clearCache, outFields).then(this, [this](FeatureQueryResult* queryResult) { // Delete result when finished. auto result = std::unique_ptr<FeatureQueryResult>(queryResult); addFeatureLayerToMap(); m_mapView->setViewpointGeometryAsync(m_wfsFeatureTable->extent()); });}
void BrowseWfsLayers::addFeatureLayerToMap(){ // create feature layer from the feature table SimpleRenderer* sr = nullptr; SimpleMarkerSymbol* sms = nullptr; SimpleLineSymbol* sls = nullptr;
// appropriate symbology for each corresponding geometry type switch (m_wfsFeatureTable->geometryType()) { case GeometryType::Point: sms = new SimpleMarkerSymbol(SimpleMarkerSymbolStyle::Circle, QColor("green"), 3.0f, this); sr = new SimpleRenderer(sms, this); break; case GeometryType::Polygon: sls = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, QColor("blue"), 3.0f, this); sr = new SimpleRenderer(sls, this); break; case GeometryType::Polyline: sls = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid, QColor("red"), 3.0f, this); sr = new SimpleRenderer(sls, this); break; default: qDebug() << "Error! No Renderer defined for this GeometryType"; return; }
// apply renderer to layer FeatureLayer* featureLayer = new FeatureLayer(m_wfsFeatureTable, this); featureLayer->setRenderer(sr);
// add the layer to the map m_map->operationalLayers()->append(featureLayer); // disable busy indicator m_loadingIndicator = false; emit isLoadingChanged();}// [WriteFile Name=BrowseWfsLayers, Category=Layers]// [Legal]// Copyright 2019 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef BROWSEWFSLAYERS_H#define BROWSEWFSLAYERS_H
// Qt headers#include <QList>#include <QObject>#include <QStringListModel>
namespace Esri::ArcGISRuntime{class Map;class MapQuickView;class WfsService;class WfsFeatureTable;class WfsLayerInfo;class LayerListModel;}
Q_MOC_INCLUDE("MapQuickView.h")
class BrowseWfsLayers : public QObject{ Q_OBJECT
Q_PROPERTY(Esri::ArcGISRuntime::MapQuickView* mapView READ mapView WRITE setMapView NOTIFY mapViewChanged) Q_PROPERTY(QStringList layerInfoTitleListModel MEMBER m_layerInfoTitleList NOTIFY layerInfoTitleListChanged) Q_PROPERTY(bool isLoading MEMBER m_loadingIndicator NOTIFY isLoadingChanged)
public: explicit BrowseWfsLayers(QObject* parent = nullptr); ~BrowseWfsLayers();
static void init();
Q_INVOKABLE void createWfsFeatureTable(int index, bool swap);
signals: void mapViewChanged(); void layerInfoTitleListChanged(); void isLoadingChanged();
private: Esri::ArcGISRuntime::MapQuickView* mapView() const; void setMapView(Esri::ArcGISRuntime::MapQuickView* mapView); void populateWfsFeatureTable(); void addFeatureLayerToMap();
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::WfsService* m_wfsService = nullptr; Esri::ArcGISRuntime::WfsFeatureTable* m_wfsFeatureTable = nullptr; QList<Esri::ArcGISRuntime::WfsLayerInfo> m_wfsLayersInfoList; QStringList m_layerInfoTitleList; bool m_loadingIndicator = false; bool m_swapAxis = false;
};
#endif // BROWSEWFSLAYERS_H// [WriteFile Name=BrowseWfsLayers, Category=Layers]// [Legal]// Copyright 2019 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport QtQuick.Layoutsimport Esri.Samples
Item { readonly property bool swapAxis: true
// add a mapView component MapView { id: view anchors.fill: parent
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); } }
BusyIndicator { id: loadingIndicator anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter running: browseWfsLayersSampleModel.isLoading; }
Rectangle { anchors { margins: 5 right: parent.right top: parent.top } width: childrenRect.width height: childrenRect.height color: "#000000" opacity: .75 radius: 5
ColumnLayout { Text { text: qsTr("Pick a WFS Layer") Layout.margins: 3 Layout.alignment: Qt.AlignHCenter color: "#ffffff" }
ComboBox { id: layersComboBox model: browseWfsLayersSampleModel.layerInfoTitleListModel Layout.fillWidth: true Layout.margins: 3 Layout.alignment: Qt.AlignHCenter
// Add a background to the ComboBox Rectangle { anchors.fill: parent radius: 10 // Make the rectangle visible if a dropdown indicator exists // An indicator only exists if a theme is set visible: parent.indicator border.width: 1 } }
Button { id: loadSelectedLayerBtn text: qsTr("Load Selected Layer") Layout.fillWidth: true Layout.margins: 3 onClicked: browseWfsLayersSampleModel.createWfsFeatureTable(layersComboBox.currentIndex, !swapAxis); }
Button { id: swapAxisOrder text: qsTr("Swap Coordinate Order") Layout.margins: 3 Layout.fillWidth: true onClicked:{ browseWfsLayersSampleModel.createWfsFeatureTable(layersComboBox.currentIndex, swapAxis); } } } }
// Declare the C++ instance which creates the map etc. and supply the view BrowseWfsLayersSample { id: browseWfsLayersSampleModel mapView: view }}// [Legal]// Copyright 2019 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "BrowseWfsLayers.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QDir>#include <QGuiApplication>#include <QQmlApplicationEngine>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("BrowseWfsLayers"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample BrowseWfsLayers::init();
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive);#endif
// Initialize application view QQmlApplicationEngine engine; // Add the import Path engine.addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml")); // Add the Runtime and Extras path engine.addImportPath(arcGISRuntimeImportPath);
// Set the source engine.load(QUrl("qrc:/Samples/Layers/BrowseWfsLayers/main.qml"));
return app.exec();}// Copyright 2019 ESRI//// All rights reserved under the copyright laws of the United States// and applicable international laws, treaties, and conventions.//// You may freely redistribute and use this sample code, with or// without modification, provided you include the original copyright// notice and use restrictions.//// See the Sample code usage restrictions document for further information.//
import QtQuick.Controlsimport Esri.Samples
ApplicationWindow { visible: true width: 800 height: 600
BrowseWfsLayers { anchors.fill: parent }}