I took one of the Feature Editing samples, and changed it so it's purely offline because for my use the user will never be editing the server directly. In the morning they will generate a new geodatabase while connected to our internal wifi, then go into the field and make edits, and then sync edits back at the office while connected to our internal wifi.
The problem I'm having is that after the geodatabase is generated, the Feature Layer is not added to the map! If I close the app and reopen it, then the layer is visible, but the replica is no longer attached for syncing.
How can I get the Feature Layers to load AFTER the geodatabase is generated? Thanks
// Copyright 2015 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 2.3
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.2
import ArcGIS.Runtime 10.26
import ArcGIS.Extras 1.0
import QtPositioning 5.7
ApplicationWindow {
id: appWindow
width: 800
height: 600
title: "Tree Trimming"
property real scaleFactor: System.displayScaleFactor
property int fontSize: 15 * scaleFactor
property string featuresUrl: "my/feature/service"
property string gdbPath: "~/ArcGIS/Runtime/Data/Test/offlineSample.geodatabase"
property var selectedFeatureId: null
Envelope {
id: initialExtent
spatialReference: map.spatialReference
xMin: -13821343.454522012
yMin: 5631086.281941083
xMax: -13732523.627654603
yMax: 5754761.393706545
}
Map {
id: map
anchors.fill: parent
focus: true
extent: initialExtent
ArcGISTiledMapServiceLayer {
url: "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer"
}
FeatureLayer {
id: offlineLayer
featureTable: local
selectionColor: "cyan"
function hitTestFeatures(x, y) {
var tolerance = Qt.platform.os === "ios"
|| Qt.platform.os === "android" ? 4 : 1
var featureIds = offlineLayer.findFeatures(
x, y, tolerance * scaleFactor, 1)
if (featureIds.length > 0) {
selectedFeatureId = featureIds[0]
selectFeature(selectedFeatureId)
statusText.text = "Tap anywhere to move the feature"
}
}
}
onMouseClicked: {
if (offlineLayer.isFeatureSelected(selectedFeatureId)) {
var featureToEdit = offlineLayer.featureTable.feature(
selectedFeatureId)
featureToEdit.geometry = mouse.mapPoint
offlineLayer.featureTable.updateFeature(selectedFeatureId,
featureToEdit)
offlineLayer.unselectFeature(selectedFeatureId)
selectedFeatureId = null
syncButton.enabled = true
statusText.text = "Tap on Sync to update the Feature Service with the edits"
} else
offlineLayer.hitTestFeatures(mouse.x, mouse.y)
}
}
GeodatabaseFeatureTable {
id: local
geodatabase: gdb.valid ? gdb : null
featureServiceLayerId: 2
}
ServiceInfoTask {
id: serviceInfoTask
url: featuresUrl
onFeatureServiceInfoStatusChanged: {
if (featureServiceInfoStatus === Enums.FeatureServiceInfoStatusCompleted) {
//statusText.text = "Service info received. Tap on the Generate Geodatabase Button"
statusText.text = gdb.valid
generateButton.enabled = true
} else if (featureServiceInfoStatus === Enums.FeatureServiceInfoStatusErrored) {
statusText.text = "Error:" + errorString
generateButton.enabled = false
cancelButton.text = "Start Over"
}
}
}
SyncGeodatabaseParameters {
id: syncGeodatabaseParameters
syncDirection: Enums.SyncDirectionBidirectional
}
Feature {
id: featureToEdit
}
Rectangle {
anchors {
fill: controlsColumn
margins: -10 * scaleFactor
}
color: "lightgrey"
radius: 5 * scaleFactor
border.color: "black"
opacity: 0.77
MouseArea {
anchors.fill: parent
onClicked: (mouse.accepted = true)
}
}
Column {
id: controlsColumn
anchors {
left: parent.left
top: parent.top
margins: 20 * scaleFactor
}
spacing: 7
Button {
id: generateButton
text: "Generate Geodatabase"
enabled: false
style: ButtonStyle {
label: Text {
text: control.text
color: control.enabled ? "black" : "grey"
horizontalAlignment: Text.AlignHCenter
}
}
onClicked: {
generateGeodatabaseParameters.initialize(
serviceInfoTask.featureServiceInfo)
generateGeodatabaseParameters.extent = map.extent
generateGeodatabaseParameters.returnAttachments = false
statusText.text = "Starting generate geodatabase task"
geodatabaseSyncTask.generateGeodatabase(
generateGeodatabaseParameters, gdbPath)
}
}
Button {
id: syncButton
text: "Sync"
width: generateButton.width
enabled: false
style: generateButton.style
onClicked: {
enabled = false
geodatabaseSyncTask.syncGeodatabase(syncGeodatabaseParameters,
gdb)
statusText.text = "Starting sync task"
}
}
Button {
id: cancelButton
text: "Cancel"
width: generateButton.width
enabled: false
style: generateButton.style
onClicked: {
geodatabaseSyncTask.cancelJob(syncStatusInfo)
enabled = false
text = "Cancel"
}
}
}
Geodatabase {
id: gdb
path: gdbPath
}
GeodatabaseSyncStatusInfo {
id: syncStatusInfo
}
GeodatabaseSyncTask {
id: geodatabaseSyncTask
url: featuresUrl
onGenerateStatusChanged: {
if (generateStatus === Enums.GenerateStatusCompleted) {
statusText.text = geodatabasePath
cancelButton.enabled = false
generateButton.enabled = false
statusText.text = "Select a feature"
} else if (generateStatus === GeodatabaseSyncTask.GenerateError) {
statusText.text = "Error: " + generateGeodatabaseError.message
+ " Code= " + generateGeodatabaseError.code.toString(
) + " " + generateGeodatabaseError.details
generateButton.enabled = false
cancelButton.text = "Start Over"
}
}
onGeodatabaseSyncStatusInfoChanged: {
if (geodatabaseSyncStatusInfo.status === Enums.GeodatabaseStatusUploadingDelta) {
var deltaProgress = geodatabaseSyncStatusInfo.deltaUploadProgress / 1000
var deltaSize = geodatabaseSyncStatusInfo.deltaSize / 1000
statusText.text = geodatabaseSyncStatusInfo.statusString + " " + String(
deltaProgress) + " of " + String(
deltaSize) + " KBs..."
} else
statusText.text = geodatabaseSyncStatusInfo.statusString + "..."
if (geodatabaseSyncStatusInfo.status !== GeodatabaseSyncStatusInfo.Cancelled)
cancelButton.enabled = true
syncStatusInfo.json = geodatabaseSyncStatusInfo.json
}
onSyncStatusChanged: {
if (syncStatus === Enums.SyncStatusCompleted) {
cancelButton.enabled = false
syncButton.enabled = false
statusText.text = "Sync completed. You may continue editing the features."
}
if (syncStatus === Enums.SyncStatusErrored)
statusText.text = "Error: " + syncGeodatabaseError.message
+ " Code= " + syncGeodatabaseError.code.toString(
) + " " + syncGeodatabaseError.details
}
}
GenerateGeodatabaseParameters {
id: generateGeodatabaseParameters
}
Rectangle {
id: textStatusRectangle
anchors {
fill: statusText
margins: -10 * scaleFactor
}
visible: statusText.text.length > 0
color: "lightgrey"
radius: 5
border.color: "black"
opacity: 0.77
}
Text {
id: statusText
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
margins: 20 * scaleFactor
}
wrapMode: Text.WordWrap
color: "black"
}
Rectangle {
anchors.fill: parent
color: "transparent"
border {
width: 0.5 * scaleFactor
color: "black"
}
}
Component.onCompleted: {
statusText.text = "Getting service info"
serviceInfoTask.fetchFeatureServiceInfo()
}
}
BUMP
The local geodatabase editing sample switches over to use the local geodatabase. It uses a ternary operator to determine if it is online or offline, and if offline, it uses the local geodatabase feature table. This all happens through property binding. If you want to be explicit in your code, you could instead dynamically create a new FeatureLayer when the generateStatusChanged signal emits, using syntax like this:
var fl = ArcGISRuntime.createObject("FeatureLayer");
fl.featureTable = local;