Update Hosted Feature Service with Feature Class

9456
24
04-18-2018 05:33 AM

Update Hosted Feature Service with Feature Class

This tool will allow you to update a hosted feature service (either an ArcGIS Online hosted feature service, or a Portal for ArcGIS hosted feature service) using a shapefile/feature class as the input.  If a unique field is specified, the existing features in the hosted feature service will be deleted that contain this same value in the feature class. If a unique field is not specified, all of the features from the shapefile/feature class will be appended to the hosted feature service.

Note:  it's recommended to create a backup of your hosted feature service before executing this tool.

Attachments
Comments

So for some reason whenever I attempt to run this tool against my feature service, which contains two layers the tool seems to delete the existing features just fine, but fails to add the new features from the feature class....

I have how I have the tool setup below, im also displaying the 14 features that should be added to the feature service in the attribute table

Yvan Sojdehei‌ can you share the hosted feature service to a Group in ArcGIS Online and invite my user account (jskinner_CountySandbox)?  I'll download the service, and then publish to my org.  Can you also zip a File Geodatabase that contains the feature class of 14 features?  You can upload and share the zipped GDB to the same Group.  I'll download that and run a test.

Done... I just sent an invitation to you to join the group. The layer is called Rigs_test and the FGDB is under Rigs_Test_gdb

Yvan Sojdehei‌ you will have to enable editing on the feature service by going to the item details > Settings tab.  Then try executing the tool again.

No Dice... I tried turning on editing and synchronized editing and it still didn't ass the features

There are a few fields that are not nullable.  For example, the Rigs_Directionals_ON_RRM_OBJECT field. 

This is preventing the feature service from updating since there is no Rigs_Directionals_ON_RRM_OBJECT field in the feature class.

Got it working for now. Thanks a lot for your help

FYI: feature service API supports Append API on the layer. This is supported in the online home APP UX as well. The API supports feature upsert where you can update existing features (using a unique field) and add new features if they do not exists. We do support most of ArcGIS data format including filegdb, shapefile, geojson, excel, csv, etc. Will be good to check it out.

https://developers.arcgis.com/rest/services-reference/append-feature-service-.htm

Thanks

Khaled

Hi Khaled Hassen,

We've been trying to use the Append API to update a hosted feature layer because of the supposed flexibility of input data and advertised high throughput. Our source data is in a file geodatabase and the hosted feature services originally published via ArcGIS Pro as uploaded service definition files (.sd). SupportsAppend is listed as true, and yet our append attempts invariably end with a generic failure with no helpful error message. We've tried using both the web interface (https://<catalog-url>/<serviceName>/FeatureServer/0/append) and the API for Python function arcgis.features.FeatureLayer.append() which we assume to be equivalent, and the results are the same.

Yet the code Jake Skinner posted above works flawlessly to accomplish the same task using https://<catalog-url>/<serviceName>/FeatureServer/0/addFeatures instead of append. We'd like to be able to compare the two methods and benchmark the throughput, but nothing we've passed as "feature collection json" to the edits parameter has been accepted - neither the output of arcgis.features.FeatureSet.from_dataframe(dataFrame).to_json, nor the JSON feature array Jake successfully posts to addFeatures. If there's any way you could share an example of acceptable JSON input (or, even better, a code snippet equivalent to Jake's code that accepts a FGDB feature class and passes it to the append function) we'd be hugely appreciative.  

Hi Torrin,

Would you be able to send me the params you have used. Also the feature service url you are trying to append to.

We doc the append API here:

Append (Feature Service)—ArcGIS REST API: Services Directory | ArcGIS for Developers 

Thanks

Khaled Hassen

Online Feature Service Lead

The service we're testing with is this one:  https://epa.maps.arcgis.com/home/item.html?id=43cb63493ebe4638ab673d05df3c99cb  and we've passed all manner of JSON to the endpoint here:

https://services.arcgis.com/cJ9YHowT8TU7DUyn/ArcGIS/rest/services/AirNowLatestpm25AppendTest/Feature... 

we typically receive a jobs URL:

https://services.arcgis.com/cJ9YHowT8TU7DUyn/ArcGIS/rest/services/AirNowLatestpm25AppendTest/Feature... 

with a status of "failed" and no error code:

Submission Time: 3/19/2019 10:26:33 PM
Last Updated Time: 3/19/2019 10:26:33 PM
Layer Name: 
Record Count: 0
Status: Failed
Error: 

Here's one example of a JSON featureset we tried - this was when we omitted OBJECTID, SHAPE_AREA, and SHAPE_LENGTH. We also tried just passing in the features array, as well as the content Jake's script saved in FeatureJSON.json that successfully posted to addFeatures.

{"objectIdFieldName":"OBJECTID","uniqueIdField":{"name":"OBJECTID","isSystemMaintained":true},"globalIdFieldName":"","geometryProperties":{"shapeAreaFieldName":"Shape__Area","shapeLengthFieldName":"Shape__Length","units":"esriMeters"},"geometryType":"esriGeometryPolygon","spatialReference":{"wkid":102100,"latestWkid":3857},"fields":[{"name":"Id","type":"esriFieldTypeInteger","alias":"Id","sqlType":"sqlTypeOther","domain":null,"defaultValue":null},{"name":"gridcode","type":"esriFieldTypeInteger","alias":"gridcode","sqlType":"sqlTypeOther","domain":null,"defaultValue":null},{"name":"Timestamp","type":"esriFieldTypeDate","alias":"Timestamp","sqlType":"sqlTypeOther","length":8,"domain":null,"defaultValue":null}],"features":[{"attributes":{"Id":300,"gridcode":5,"Timestamp":1552939200000},"geometry":{"rings":[[[-11324287.0621476,7152262.28239157],[-11368388.4322056,7154791.31914],[-11392533.4617857,7174706.89550593],[-11417485.3946678,7215621.58606784],[-11430608.0850955,7267694.89738161],[-11431351.9177922,7330707.01600218],[-11425947.8681333,7369298.32746687],[-11412717.0566676,7410131.25054272],[-11399578.3483084,7434157.30438544],[-11377742.9042346,7460248.19390435],[-11343788.8938177,7475627.99730598],[-11313714.2261996,7467346.84814433],[-11288766.2978003,7445987.45914782],[-11266337.1891383,7408101.26774228],[-11254011.3908055,7366248.5444516],[-11247898.5476753,7314356.77282235],[-11254666.1237579,7259847.2697525],[-11264715.3735682,7224280.80451561],[-11279059.431277,7194633.62836684],[-11299925.7904916,7167658.10217196],[-11315493.2177231,7158601.70096791],[-11323058.6870214,7154430.94987946],[-11324287.0621476,7152262.28239157]]]}},{"attributes":{"Id":400,"gridcode":4,"Timestamp":1552939200000},"geometry":{"rings":[[[-11324287.0621476,7152262.28239157],[-11368388.4322056,7154791.31914],[-11392533.4617857,7174706.89550593],[-11417485.3946678,7215621.58606784],[-11430608.0850955,7267694.89738161],[-11431351.9177922,7330707.01600218],[-11425947.8681333,7369298.32746687],[-11412717.0566676,7410131.25054272],[-11399578.3483084,7434157.30438544],[-11377742.9042346,7460248.19390435],[-11343788.8938177,7475627.99730598],[-11313714.2261996,7467346.84814433],[-11288766.2978003,7445987.45914782],[-11266337.1891383,7408101.26774228],[-11254011.3908055,7366248.5444516],[-11247898.5476753,7314356.77282235],[-11254666.1237579,7259847.2697525],[-11264715.3735682,7224280.80451561],[-11279059.431277,7194633.62836684],[-11299925.7904916,7167658.10217196],[-11315493.2177231,7158601.70096791],[-11323058.6870214,7154430.94987946],[-11324287.0621476,7152262.28239157]]]}}]}

When trying using the API for Python, we used a sequence like this:

import arcgis
gis = arcgis.GIS('https://epa.maps.arcgis.com',"OAR_OAQPS_EPA","*********")
item = gis.content.search('title:"AirNowLatestpm25AppendTest" AND owner:"OAR_OAQPS_EPA"', item_type="Feature Layer")[0]
flyr = item.layers[0]
dataFrame = arcgis.features.GeoAccessor.from_featureclass(r'D:\***Path***\AppendTest.gdb\AirNow24Hr_PM25AppendTest')
arcgisFeatureSet = arcgis.features.FeatureSet.from_dataframe(dataFrame)
featureSetJSON = arcgisFeatureSet.to_json
flyr.append(edits=featureSetJSON,upload_format="featureCollection")

which generated a dead-end error:

Object reference not set to an instance of an object.
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
  File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\features\layer.py", line 1085, in append
    sres = self._con.get(path=surl, params={'f' : 'json'})
  File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\connection.py", line 880, in get
    self._handle_json_error(resp_json['error'], errorcode)
  File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\connection.py", line 1180, in _handle_json_error
    raise RuntimeError(errormessage)
RuntimeError: Object reference not set to an instance of an object.
(Error Code: 500)

The documentation at the URL you refer to simply says that "Only feature collection is supported" via the edits parameter, but "feature collection" is apparently a pretty loosely defined structure - even ArcPy and the API for Python don't agree on a definition:

https://community.esri.com/thread/211220-arcgisfeatureset-arcpyfeatureset 

So what syntax does Append expect here?

What you are passing in there is not esri feature collection. What I would suggest is to start using uploadId or itemId in the append API.

So you can just go the feature service uploads API. Upload your filegdb and then use append passing in the uploadId for your filegdb.

So the UX append does not work from filegdb or other data source?

This is what the append expects as a feature collection.

{
"layers": [
{
"layerDefinition": {
"currentVersion": 10.2,
"id": 0,
"name": "CITIES",
"displayField": "",
"description": "",
"copyrightText": "",
"defaultVisibility": true,
"editFieldsInfo": null,
"relationships": [

],
"isDataVersioned": false,
"supportsRollbackOnFailureParameter": true,
"supportsAdvancedQueries": true,
"geometryType": "esriGeometryPoint",
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": {
"type": "esriSMS",
"style": "esriSMSCircle",
"color": [
255,
85,
0,
255
],
"size": 7,
"angle": 0,
"xoffset": 0,
"yoffset": 0,
"outline": {
"color": [
168,
0,
0,
255
],
"width": 1
}
},
"label": "",
"description": ""
},
"transparency": 0,
"labelingInfo": null
},
"allowGeometryUpdates": true,
"hasAttachments": false,
"htmlPopupType": "esriServerHTMLPopupTypeNone",
"hasM": false,
"hasZ": false,
"globalIdField": "GlobalID",
"typeIdField": "",
"types": [

],
"templates": [
{
"name": "New Feature",
"description": "",
"drawingTool": "esriFeatureEditToolPoint",
"prototype": {
"attributes": {
"NAME": null,
"COUNTRY": null,
"Order": null,
"CAPITAL": null
}
}
}
],
"supportedQueryFormats": "JSON",
"hasStaticData": false,
"maxRecordCount": -1,
"capabilities": "Create,Delete,Query,Update,Editing",
"fields": [
{
"name": "FID",
"type": "esriFieldTypeOID",
"alias": "FID"
},
{
"name" : "GlobalID",
"type" : "esriFieldTypeGlobalID",
"alias" : "GlobalID"
},
{
"name": "NAME",
"type": "esriFieldTypeString",
"alias": "NAME",
"length": 40
},
{
"name": "COUNTRY",
"type": "esriFieldTypeString",
"alias": "COUNTRY",
"length": 12
},
{
"name": "Order",
"type": "esriFieldTypeDouble",
"alias": "Order"
},
{
"name": "CAPITAL",
"type": "esriFieldTypeString",
"alias": "CAPITAL",
"length": 1
}
],
"objectIdField": "FID",
"minScale": 0,
"maxScale": 0,
"extent": {
"xmin": -18397772.7190125,
"ymin": -7010792.484942091,
"xmax": 19718042.33097229,
"ymax": 14476655.301642163,
"spatialReference": {
"wkid": 102100,
"latestWkid": 3857
}
},
"type": "Feature Layer"
},
"featureSet": {
"geometryType": "esriGeometryPoint",
"spatialReference": {
"wkid": 102100,
"latestWkid": 3857
},
"features": [
{
"attributes": {
"FID": 533,
"GlobalID": "{CE7011B8-FB6A-4ADA-B1A1-15410EB30155}",
"NAME": "Shijiazhuang",
"COUNTRY": "China",
"Order": 1190000,
"CAPITAL": "N"
},
"geometry": {
"x": 12752728.320089377,
"y": 4590322.487161252,
"spatialReference": {
"wkid": 102100,
"latestWkid": 3857
}
}
}
]
}
}
],
"showLegend": true
}

Given that schema, and without a tool to convert an APRX map to a feature collection, I agree that uploading and then referencing the uploaded ID does seem more practical. I was able to accomplish this manually using the REST UX:

https://services.arcgis.com/cJ9YHowT8TU7DUyn/ArcGIS/rest/services/AirNowLatestpm25AppendTest/Feature... 

and both versions of web-based append:

https://services.arcgis.com/cJ9YHowT8TU7DUyn/arcgis/rest/services/AirNowLatestpm25AppendTest/Feature... 

https://services.arcgis.com/cJ9YHowT8TU7DUyn/arcgis/rest/services/AirNowLatestpm25AppendTest/Feature... 

But am having less success with the API for Python, which doesn't appear to have any equivalent to FeatureServer/append, only FeatureServer/0/append (arcgis.features.FeatureLayer.append), and doesn't seem to have an equivalent to the appendUploadId parameter, only itemID. So when we try the same steps at the command line (same feature service, same uploaded zipped FGDB, same data), it crashes:

*** Python 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 11:27:44) [MSC v.1900 64 bit (AMD64)] on win32. ***
*** Remote Python engine is active ***
>>> import arcgis
>>> gis = arcgis.GIS('https://epa.maps.arcgis.com',"OAR_OAQPS_EPA","**********")
>>> res = gis.content.search('title:"AirNowLatestpm25AppendTest" AND owner:"OAR_OAQPS_EPA"', item_type="Feature Layer")[0]
>>> flcoll = arcgis.features.FeatureLayerCollection.fromitem(res)
>>> flcoll.upload(r'D:\path\AppendTest.gdb.zip',)
(True,
 {'item': {'committed': True,
 'date': 1553202428922,
 'description': '',
 'itemID': '7515770064ab46bb947229fa993e9960',
 'itemName': 'AppendTest.gdb.zip',
 'serviceName': 'AirNowLatestpm25AppendTest.FeatureServer'},
 'success': True})
>>> fl = res.layers[0]
>>> fl.append(source_table_name="AirNowLatest_pm25",upload_format="filegdb",item_id='7515770064ab46bb947229fa993e9960')
Object reference not set to an instance of an object.
Traceback (most recent call last):
 File "<interactive input>", line 1, in <module>
 File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\features\layer.py", line 1085, in append
 sres = self._con.get(path=surl, params={'f' : 'json'})
 File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\connection.py", line 880, in get
 self._handle_json_error(resp_json['error'], errorcode)
 File "D:\public\server\ArcGISPro\bin\Python\envs\arcgispro-py3\lib\site-packages\arcgis\_impl\connection.py", line 1180, in _handle_json_error
 raise RuntimeError(errormessage)
RuntimeError: Object reference not set to an instance of an object.
(Error Code: 400)

This seems to be the same problem described in this forum posting:

https://community.esri.com/thread/209802-object-reference-not-set-to-an-instance-of-an-object-when-u... 

So our best bet is probably to just skip the API for Python and use the REST API from Python directly.  

Hi Chris,

Can you share the service with a Group in AGOL and invite my user account (jskinner_CountySandbox)?  I can download the data and re-publish.  Could you also include in the group the zipped File Geodatabase that contains the feature class you are trying to use to update this feature service?

Can you enable others to export for the CIP_Sandbox_1_gdb layer?  I will need to make a local copy of this layer.

Chris Jones‌ I'm having trouble reproducing this.  I downloaded the data, and then created a hosted feature service in AGOL from the one layer.  I then executed the tool with the following parameters:

This executed successfully:

Jake, is this written only for ArcGIS Pro or will it also work in IDLE?

GIS Admin IDFBINS‌ this can be run in IDLE.  You will just need to update the # Variables section in the code:

Good Morning, 

When I run the script from arccatalog it displays a message processing objectid's, reading JSON files, adding features.... then 0 features added.  am i missing something have tested on an empty dataset too, I can successfully use flayer,delete_features and append but not this, is it something to do with the objectid?

Cheers

Rory

Rory Bennison‌ try the follow script instead:

https://community.esri.com/docs/DOC-14843-overwrite-arcgis-online-feature-service-using-truncate-and... 

It leverages the ArcGIS API for Python and is much faster.

@JakeSkinner I tried the code from the link below for Portal 10.6.1.  The truncate works but the append is only a AGOL feature.  bummer...

https://community.esri.com/docs/DOC-14843-overwrite-arcgis-online-feature-service-using-truncate-and... 

@ToddMcNeilyou can use ArcGIS Pro's Delete Rows and Append GP tools instead.  They will work on a feature service just as if it were a feature class.

Hi @JakeSkinner, I have recently been having issues with Field Maps data syncing to it's hosted feature layer. There error I am receiving can be found in this link. Sync Failed - Request Forbidden - Esri Community

The tool was successful in syncing the data that I have been  having the error with. However, it did not sync the attachments that are attached to the feature. Is there a solution for this?

Version history
Last update:
‎04-18-2018 05:33 AM
Updated by:
Contributors