Start a local feature service and display its features in a map.
Use case
For executing offline geoprocessing tasks in your apps via an offline (local) server.
How to use the sample
A Local Server and Local Feature Service will automatically be started. Once started then a FeatureLayer
will be created and added to the map.
How it works
- Create and run the Local Server.
LocalServer::instance
creates the Local Server.LocalServer::start()
starts the server asynchronously.
- Wait for server to be in the
LocalServerStatus::Started
state.LocalServer::statusChanged()
fires whenever the running status of the Local Server changes.
- Create and run a local service. For example, to run a
LocalMapService
:new LocalFeatureService(Url)
creates a local feature service with the given URL path to the map package (mpkx
file).LocalFeatureService::start()
starts the service asynchronously.- The service is added to the Local Server automatically.
- Wait for feature service to be in the
LocalServerStatus::Started
state.LocalFeatureService::statusChanged()
signal fires whenever the status of the Local Service changes.
- Create a feature layer from local feature service.
- Create a
ServiceFeatureTable(Url)
from local feature service URL,LocalFeatureService::url()
. - Create a
FeatureLayer
using the service feature table. - Add the feature layer to the map's operational layers.
- Connect to the feature layer's
LoadStatusChanged
signal. - When the feature layer's status is
Loaded
, set the map view's extent to the layer's full extent.
- Create a
Relevant API
- FeatureLayer
- LocalFeatureService
- LocalFeatureService::statusChanged
- LocalServer
- LocalServerStatus
Offline Data
Read more about how to set up the sample's offline data here.
Link | Local Location |
---|---|
PointsOfInterest Pro Map Package | <userhome> /ArcGIS/Runtime/Data/mpkx/PointsofInterest.mpkx |
Additional information
Local Server can be downloaded for Windows and Linux platforms. Local Server is not supported on macOS.
Tags
feature service, local, offline, server, service
Sample Code
// [WriteFile Name=LocalServerFeatureLayer, Category=LocalServer] // [Legal] // Copyright 2017 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 "LocalServerFeatureLayer.h" // ArcGIS Maps SDK headers #include "Basemap.h" #include "Envelope.h" #include "FeatureLayer.h" #include "LayerListModel.h" #include "LocalFeatureService.h" #include "LocalServer.h" #include "LocalServerTypes.h" #include "Map.h" #include "MapQuickView.h" #include "MapTypes.h" #include "MapViewTypes.h" #include "ServiceFeatureTable.h" #include "Viewpoint.h" // Qt headers #include <QDir> #include <QFile> #include <QFuture> #include <QTemporaryDir> using namespace Esri::ArcGISRuntime; LocalServerFeatureLayer::LocalServerFeatureLayer(QQuickItem* parent) : QQuickItem(parent) { // Create a temporary directory for the local server if one has not already been created if (!LocalServer::appDataPath().isEmpty() && !LocalServer::tempDataPath().isEmpty()) return; // create temp/data path const QString tempPath = LocalServerFeatureLayer::shortestTempPath() + "/EsriQtTemp"; // create the directory m_tempDir = std::make_unique<QTemporaryDir>(tempPath); // set the temp & app data path for the local server LocalServer::instance()->setTempDataPath(m_tempDir->path()); LocalServer::instance()->setAppDataPath(m_tempDir->path()); } LocalServerFeatureLayer::~LocalServerFeatureLayer() = default; void LocalServerFeatureLayer::init() { qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<LocalServerFeatureLayer>("Esri.Samples", 1, 0, "LocalServerFeatureLayerSample"); } void LocalServerFeatureLayer::componentComplete() { QQuickItem::componentComplete(); // find QML MapView component m_mapView = findChild<MapQuickView*>("mapView"); m_mapView->setWrapAroundMode(WrapAroundMode::Disabled); // Create a map using the topographic BaseMap m_map = new Map(BasemapStyle::ArcGISTopographic, this); // Set map to map view m_mapView->setMap(m_map); // Check for ArcGIS Pro map package files const QString fileName = "PointsofInterest.mpkx"; const QString dataPath = QDir::homePath() + "/ArcGIS/Runtime/Data/mpkx/" + fileName; // Check to see if map package exists if (!QFile::exists(dataPath)) { qDebug() << "ArcGIS Pro .mpkx file not found at" << dataPath; } // create a feature service m_localFeatureService = new LocalFeatureService(dataPath, this); if (LocalServer::instance()->isInstallValid()) { connectSignals(); if (LocalServer::status() == LocalServerStatus::Started) { qDebug() << "Local server was already running"; // The local server can already be running if it was started in a different application. // We start up the local feature service here because there will be no status change signal to otherwise trigger it. startFeatureService(); } else if (LocalServer::status() != LocalServerStatus::Starting) { LocalServer::start(); } } else qDebug() << "Local server install invalid"; } void LocalServerFeatureLayer::connectSignals() { // Local server status connect(LocalServer::instance(), &LocalServer::statusChanged, this, [this]() { // If the local server was not previously running, our local feature service will start here if the local server has successfully started. if (LocalServer::status() == LocalServerStatus::Started) { startFeatureService(); } }); // local feature service status connect(m_localFeatureService, &LocalFeatureService::statusChanged, this, [this]() { if (m_localFeatureService->status() == LocalServerStatus::Starting) { qDebug() << "Local feature service starting up"; } else if (m_localFeatureService->status() == LocalServerStatus::Started) { qDebug() << "Successfully hosting local feature service at:" << m_localFeatureService->url().toString(); // create the feature layer ServiceFeatureTable* svt = new ServiceFeatureTable(QUrl(m_localFeatureService->url().toString() + "/0"), this); FeatureLayer* featureLayer = new FeatureLayer(svt, this); connect(featureLayer, &FeatureLayer::loadStatusChanged, this, [this, featureLayer](LoadStatus status) { if (status == LoadStatus::Loaded) { m_mapView->setViewpointAsync(Viewpoint(featureLayer->fullExtent())); } }); m_map->operationalLayers()->append(featureLayer); } else if (m_localFeatureService->status() == LocalServerStatus::Failed) { qDebug() << "Local feature service failed to start"; } }); } void LocalServerFeatureLayer::startFeatureService() const { if (m_localFeatureService->status() != LocalServerStatus::Started || m_localFeatureService->status() != LocalServerStatus::Starting) m_localFeatureService->start(); } QString LocalServerFeatureLayer::shortestTempPath() { // get tmp and home paths const QString tmpPath = QDir::tempPath(); const QString homePath = QDir::homePath(); // return whichever is shorter, temp or home path if (homePath.length() > tmpPath.length()) return tmpPath; else return homePath; }