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 }}