Have been tinkering with this for the better part of a day and can't figure it out.
I'm calling an API to get a json response containing store locations, which contain the coordinates, then pushing that into a Feature Layer. Esri's implementation of the Spatially Enabled Dataframe's .to_featurelayer has the unfortunate behavior of sanitizing column names, even if the column names are valid to begin with.
The result is that its changing my column names from their original case to snake_case. For example 'storeName' gets changed to 'store_name' when I utilize the .to_featurelayer method to publish the data frame as a feature layer, even though there is absolutely nothing invalid at all about a feature layer with a field named 'storeName'.
Unlike the .to_featureclass method, where sanitize_columns is exposed as a parameter in the method and defaulted to True, meaning you can set it to False to avoid this behavior, sanitize_columns is defaulted to to True in the .to_featurelayer method and is not exposed as a parameter so there is no way to avoid it.
As a result, I'm trying to go back and update the column names using the update_definition method on the feature layer manager but I keep getting one of two errors.
The first approach I took:
from arcgis.features import FeatureLayer
featureLayer =gis.content.get("c952e9e257bd4fc887be2934291548cb")
lyr = featureLayer.layers[0]
lyr.properties
originalDefinition = lyr.properties
# Get the fields array
originalFields = originalDefinition["fields"]
newFields = originalFields.copy()
for f in newFields:
if "_" in f["name"]:
newFieldName = f["name"].split("_")[0].lower()+f["name"].split("_")[1].title()
f["name"] = newFieldName
#newFields
print('"fields": ' + json.dumps(newFields))
lyr.manager.update_definition('"fields": ' + json.dumps(newFields))
Fails with:
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-28-34cef8ca738c> in <module>
13 #newFields
14 print('"fields": ' + json.dumps(newFields))
---> 15 lyr.manager.update_definition('"fields": ' + json.dumps(newFields))
/opt/conda/lib/python3.7/site-packages/arcgis/features/managers.py in update_definition(self, json_dict)
2002 u_url = self._url + "/updateDefinition"
2003
-> 2004 res = self._con.post(u_url, params)
2005 self.refresh()
2006 return res
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in post(self, path, params, files, **kwargs)
718 file_name=file_name,
719 try_json=try_json,
--> 720 force_bytes=kwargs.pop('force_bytes', False))
721 #----------------------------------------------------------------------
722 def put(self, url, params=None, files=None, **kwargs):
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in _handle_response(self, resp, file_name, out_path, try_json, force_bytes)
512 return data
513 errorcode = data['error']['code'] if 'code' in data['error'] else 0
--> 514 self._handle_json_error(data['error'], errorcode)
515 return data
516 else:
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in _handle_json_error(self, error, errorcode)
534
535 errormessage = errormessage + "\n(Error Code: " + str(errorcode) +")"
--> 536 raise Exception(errormessage)
537 #----------------------------------------------------------------------
538 def post(self,
Exception: Unable to update feature service layer definition.
Object reference not set to an instance of an object.
(Error Code: 400)
Kinda makes sense. Maybe I need to instantiate a FeatureLayer object on the actual layer...Let's try that:
featureLayer =gis.content.get("c952e9e257bd4fc887be2934291548cb")
lyr = FeatureLayer(featureLayer.layers[0])
originalDefinition = lyr.properties
# Get the fields array
originalFields = originalDefinition["fields"]
newFields = originalFields.copy()
for f in newFields:
if "_" in f["name"]:
newFieldName = f["name"].split("_")[0].lower()+f["name"].split("_")[1].title()
f["name"] = newFieldName
newFields
print('"fields": ' + json.dumps(newFields))
#lyr.manager.update_definition('"fields": ' + json.dumps(newFields))
But it fails with:
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
/opt/conda/lib/python3.7/site-packages/arcgis/gis/__init__.py in _hydrate(self)
11481 if isinstance(self._con, Connection):
> 11482 self._lazy_token = self._con.generate_portal_server_token(serverUrl=self._url)
11483 else:
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in generate_portal_server_token(self, serverUrl, expiration)
1313 resp = self.post(path=self._token_url, postdata=postdata,
-> 1314 ssl=True, add_token=False)
1315 if isinstance(resp, dict) and resp:
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in post(self, path, params, files, **kwargs)
719 try_json=try_json,
--> 720 force_bytes=kwargs.pop('force_bytes', False))
721 #----------------------------------------------------------------------
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in _handle_response(self, resp, file_name, out_path, try_json, force_bytes)
513 errorcode = data['error']['code'] if 'code' in data['error'] else 0
--> 514 self._handle_json_error(data['error'], errorcode)
515 return data
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in _handle_json_error(self, error, errorcode)
535 errormessage = errormessage + "\n(Error Code: " + str(errorcode) +")"
--> 536 raise Exception(errormessage)
537 #----------------------------------------------------------------------
Exception: Unable to generate token.
'username' must be specified.
'password' must be specified.
'referer' must be specified.
(Error Code: 400)
During handling of the above exception, another exception occurred:
AttributeError Traceback (most recent call last)
<ipython-input-27-77bf06c45026> in <module>
2 featureLayer =gis.content.get("c952e9e257bd4fc887be2934291548cb")
3 lyr = FeatureLayer(featureLayer.layers[0], gis=gis)
----> 4 lyr.properties
5 originalDefinition = lyr.properties
6 # Get the fields array
/opt/conda/lib/python3.7/site-packages/arcgis/gis/__init__.py in properties(self)
11460 return self._lazy_properties
11461 else:
> 11462 self._hydrate()
11463 return self._lazy_properties
11464
/opt/conda/lib/python3.7/site-packages/arcgis/gis/__init__.py in _hydrate(self)
11507 # try as a public server
11508 self._lazy_token = None
> 11509 self._refresh()
11510
11511 except HTTPError as httperror:
/opt/conda/lib/python3.7/site-packages/arcgis/gis/__init__.py in _refresh(self)
11450 dictdata = self._con.get(self.url, params)
11451 else:
> 11452 raise e
11453
11454 self._lazy_properties = PropertyMap(dictdata)
/opt/conda/lib/python3.7/site-packages/arcgis/gis/__init__.py in _refresh(self)
11443 else:
11444 try:
> 11445 dictdata = self._con.post(self.url, params, token=self._lazy_token)
11446 except Exception as e:
11447 if hasattr(e, 'msg') and e.msg == "Method Not Allowed":
/opt/conda/lib/python3.7/site-packages/arcgis/gis/_impl/_con/_connection.py in post(self, path, params, files, **kwargs)
619 try_json = kwargs.pop("try_json", True)
620 add_token = kwargs.pop('add_token', True)
--> 621 if url.find('://') == -1:
622 url = self._baseurl + url
623 if kwargs.pop("ssl", False) or self._all_ssl:
AttributeError: 'FeatureLayer' object has no attribute 'find'
I don't understand why I would need to authenticate with credentials manually here or even how I would. I have a connection to a GIS object and even passing that in as the gis parameter on the instantiation of the FeatureLayer object at line 3 doesn't change result.
I also tried using FeatureCollection but got the same result in both approaches.
Help or insight appreciated.