1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
// [WriteFile Name=EditWithBranchVersioning, Category=EditData]
// [Legal]
// Copyright 2020 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
#include "EditWithBranchVersioning.h"
#include "AuthenticationManager.h"
#include "FeatureEditResult.h"
#include "FeatureLayer.h"
#include "FeatureTableEditResult.h"
#include "Layer.h"
#include "Map.h"
#include "MapQuickView.h"
#include "ServiceFeatureTable.h"
#include "ServiceGeodatabase.h"
#include "ServiceVersionInfo.h"
#include "ServiceVersionParameters.h"
#include "MapTypes.h"
#include "Credential.h"
#include "TaskWatcher.h"
#include "IdentifyLayerResult.h"
#include "ArcGISFeature.h"
#include "AttributeListModel.h"
#include "CalloutData.h"
#include "LayerListModel.h"
#include "ServiceTypes.h"
#include "Point.h"
#include "Envelope.h"
#include "Viewpoint.h"
#include "Polyline.h"
using namespace Esri::ArcGISRuntime;
namespace
{
// Convenience RAII struct that deletes all pointers in given container.
struct FeatureTableEditResultsScopedCleanup
{
FeatureTableEditResultsScopedCleanup(const QList<FeatureTableEditResult*>& list) : results(list) { }
~FeatureTableEditResultsScopedCleanup() { qDeleteAll(results); }
const QList<FeatureTableEditResult*>& results;
};
}
EditWithBranchVersioning::EditWithBranchVersioning(QObject* parent /* = nullptr */):
QObject(parent),
m_map(new Map(BasemapStyle::ArcGISStreets, this)),
m_cred(new Credential("editor01", "S7#i2LWmYH75", this))
{
}
EditWithBranchVersioning::~EditWithBranchVersioning() = default;
void EditWithBranchVersioning::init()
{
// Register the map view for QML
qmlRegisterUncreatableType<AuthenticationManager>("Esri.Samples", 1, 0, "AuthenticationManager", "AuthenticationManager is uncreateable");
qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView");
qmlRegisterType<EditWithBranchVersioning>("Esri.Samples", 1, 0, "EditWithBranchVersioningSample");
}
MapQuickView* EditWithBranchVersioning::mapView() const
{
return m_mapView;
}
// Set the view (created in QML)
void EditWithBranchVersioning::setMapView(MapQuickView* mapView)
{
if (!mapView || mapView == m_mapView)
return;
m_mapView = mapView;
m_mapView->setMap(m_map);
m_busy = true;
emit busyChanged();
// When map is done loading set up service geodatabse signals and map signals
connect(m_map, &Map::doneLoading, this, &EditWithBranchVersioning::onMapDoneLoading);
emit mapViewChanged();
}
AuthenticationManager *EditWithBranchVersioning::authManager() const
{
return AuthenticationManager::instance();
}
void EditWithBranchVersioning::onMapDoneLoading(const Error& error)
{
if (!error.isEmpty())
return;
// connect all needed service geodatabase signals
connectSgdbSignals();
connect(m_mapView, &MapQuickView::mouseClicked, this, [this](QMouseEvent& mouseEvent)
{
m_busy = true;
emit busyChanged();
// first clear the selection
m_featureLayer->clearSelection();
emit hideWindow();
// if feature is already selected, then move to new location
if (m_selectedFeature)
{
if (m_serviceGeodatabase->versionName() == m_serviceGeodatabase->defaultVersionName())
{
clearSelectedFeature();
m_busy = false;
emit busyChanged();
return;
}
const Point clickedPoint = m_mapView->screenToLocation(mouseEvent.position().x(), mouseEvent.position().y());
moveFeature(clickedPoint);
}
else
{
// call identify on the map view
m_mapView->identifyLayer(m_featureLayer, mouseEvent.position().x(), mouseEvent.position().y(), 5, false, 1);
}
});
connect(m_mapView, &MapQuickView::viewpointChanged, this, [this]()
{
if (!m_featureLayer)
return;
m_featureLayer->clearSelection();
clearSelectedFeature();
emit hideWindow();
});
// connect to the identifyLayerCompleted signal on the map view
connect(m_mapView, &MapQuickView::identifyLayerCompleted, this, [this](const QUuid&, IdentifyLayerResult* identifyResult)
{
if (!identifyResult)
{
clearSelectedFeature();
return;
}
if (!identifyResult->geoElements().empty())
{
m_selectedFeature = static_cast<ArcGISFeature*>(identifyResult->geoElements().at(0));
// select the item in the result
m_featureLayer->selectFeature(m_selectedFeature);
// Obtain placename attribute for the callout title
const QString featureName = m_selectedFeature->attributes()->attributeValue("PLACENAME").toString();
m_mapView->calloutData()->setTitle(QString("<br><font size=\"+2\">%1</font>").arg(featureName));
m_mapView->calloutData()->setLocation(m_selectedFeature->geometry().extent().center());
m_currentTypeDamage = m_selectedFeature->attributes()->attributeValue("TYPDAMAGE").toString();
emit currentTypeDamageChanged();
emit featureSelected();
}
m_busy = false;
emit busyChanged();
});
}
void EditWithBranchVersioning::connectSgdbSignals()
{
m_serviceGeodatabase = new ServiceGeodatabase(QUrl("https://sampleserver7.arcgisonline.com/server/rest/services/DamageAssessment/FeatureServer"), m_cred, this);
m_busy = true;
emit busyChanged();
connect(m_serviceGeodatabase, &ServiceGeodatabase::doneLoading, this, &EditWithBranchVersioning::onSgdbDoneLoadingCompleted);
connect(m_serviceGeodatabase, &ServiceGeodatabase::createVersionCompleted, this, &EditWithBranchVersioning::onCreateVersionCompleted);
connect(m_serviceGeodatabase, &ServiceGeodatabase::applyEditsCompleted, this, [this](const QUuid&,const QList<FeatureTableEditResult*>& featureTableEditResults)
{
// A convenience wrapper that deletes the contents of featureEditResults when we leave scope.
FeatureTableEditResultsScopedCleanup featureTableEditResultsCleanup(featureTableEditResults);
for (FeatureTableEditResult* featureTableEditResult : featureTableEditResults)
{
const auto results = featureTableEditResult->editResults();
for (FeatureEditResult* featureEditResult : results)
{
if (!featureEditResult->error().isEmpty())
{
m_errorMessage = featureEditResult->error().message() + " - " + featureEditResult->error().additionalMessage();
emit errorMessageChanged();
m_busy = false;
emit busyChanged();
return;
}
}
}
emit applyingEditsCompleted();
switchVersion();
});
connect(m_serviceGeodatabase, &ServiceGeodatabase::errorOccurred, this, [this](const Error& error)
{
m_errorMessage = error.message() + " - " + error.additionalMessage();
emit errorMessageChanged();
m_busy = false;
emit busyChanged();
});
connect(m_serviceGeodatabase, &ServiceGeodatabase::switchVersionCompleted, this, [this](const QUuid&)
{
m_busy = false;
emit busyChanged();
// set the current version name for the UI
m_sgdbCurrentVersionName = m_serviceGeodatabase->versionName();
// if the current version is the default, prevent editing
m_sgdbVersionIsDefault = m_sgdbCurrentVersionName == m_serviceGeodatabase->defaultVersionName() ? true : false;
clearSelectedFeature();
emit sgdbVersionIsDefaultChanged();
emit sgdbCurrentVersionChanged();
});
m_serviceGeodatabase->load();
}
void EditWithBranchVersioning::onSgdbDoneLoadingCompleted(const Error& error)
{
if (!error.isEmpty())
return;
// created service feature table from the table contained in the service geodatabase
m_featureTable = m_serviceGeodatabase->table(0);
connect(m_featureTable, &ServiceFeatureTable::doneLoading, this, [this](const Error& error)
{
if (!error.isEmpty())
return;
// once the service feature table is loaded set the mapview extent to the full extent of the feature layer
m_mapView->setViewpoint(m_featureLayer->fullExtent());
m_busy = false;
emit busyChanged();
});
connect(m_featureTable, &ServiceFeatureTable::updateFeatureCompleted, this, [this](const QUuid&, bool)
{
m_busy = false;
emit busyChanged();
});
// create a feature layer from the service feature table
m_featureLayer = new FeatureLayer(m_featureTable, this);
// add the feature layer to the map
m_map->operationalLayers()->append(m_featureLayer);
// update current version for UI
m_sgdbCurrentVersionName = m_serviceGeodatabase->versionName();
emit sgdbCurrentVersionChanged();
}
void EditWithBranchVersioning::onCreateVersionCompleted(const QUuid&, Esri::ArcGISRuntime::ServiceVersionInfo* serviceVersionInfo)
{
// check for local edits before switching versions
if (m_serviceGeodatabase->hasLocalEdits())
return;
// ensure the created version was succesful
if (!serviceVersionInfo)
return;
m_busy = false;
emit busyChanged();
emit createVersionSuccess();
// store create version name for easy switching between default and created version
m_createdVersionName = serviceVersionInfo->name();
// switch to the version you just created
m_serviceGeodatabase->switchVersion(m_createdVersionName);
}
void EditWithBranchVersioning::applyEdits()
{
m_busy = true;
emit busyChanged();
if (m_serviceGeodatabase->hasLocalEdits())
{
emit applyingEdits();
m_serviceGeodatabase->applyEdits();
}
else
switchVersion();
}
void EditWithBranchVersioning::switchVersion() const
{
// if the current version is our created version switch to the default
if (m_serviceGeodatabase->versionName() == m_serviceGeodatabase->defaultVersionName())
m_serviceGeodatabase->switchVersion(m_createdVersionName);
else
m_serviceGeodatabase->switchVersion(m_serviceGeodatabase->defaultVersionName());
}
void EditWithBranchVersioning::updateAttribute(const QString& attributeValue)
{
m_busy = true;
emit busyChanged();
if (!m_selectedFeature)
return;
if (sgdbVerionIsDefault())
{
clearSelectedFeature();
return;
}
// update the attirbute with the selection from the combo box
m_selectedFeature->attributes()->replaceAttribute("TYPDAMAGE", attributeValue);
m_selectedFeature->featureTable()->updateFeature(m_selectedFeature);
}
void EditWithBranchVersioning::createVersion(const QString& versionName, const QString& versionAccess, const QString& description)
{
m_busy = true;
emit busyChanged();
// create parameters for version from the inputed fields from the UI
ServiceVersionParameters* params = new ServiceVersionParameters(this);
params->setName(versionName);
params->setDescription(description);
if (versionAccess == "Private")
params->setAccess(VersionAccess::Private);
else if (versionAccess == "Protected")
params->setAccess(VersionAccess::Protected);
else
params->setAccess(VersionAccess::Public);
m_serviceGeodatabase->createVersion(params);
}
void EditWithBranchVersioning::moveFeature(const Point& mapPoint)
{
if (sgdbVerionIsDefault())
{
clearSelectedFeature();
return;
}
const Polyline geom = geometry_cast<Polyline>(m_selectedFeature->geometry());
m_selectedFeature->setGeometry(mapPoint);
// update the selected feature with the new geometry
m_selectedFeature->featureTable()->updateFeature(m_selectedFeature);
clearSelectedFeature();
}
void EditWithBranchVersioning::clearSelection()
{
// helper function to clear feature layer selection
if (m_featureLayer)
m_featureLayer->clearSelection();
}
bool EditWithBranchVersioning::sgdbVerionIsDefault() const
{
// helper function to check if the current version is the default version
return m_serviceGeodatabase->versionName() == m_serviceGeodatabase->defaultVersionName() ? true : false;
}
void EditWithBranchVersioning::clearSelectedFeature()
{
// helper function to clean up the selected feature when invalidated
delete m_selectedFeature;
m_selectedFeature = nullptr;
clearSelection();
}