I am trying to write a script that can be automated and will pull down a feature layer, check a field, alter the value based on a condition, and then updates the feature layer. To do this, I am using (generalized):
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
from arcgis.features import GeoAccessor, GeoSeriesAccessor
gis = GIS(URL, username, password)
item1content = gis.content.get(item1id)
item1 = item1content.layers[0]
df = points_layer.query().sdf
# complicated bit of logic
# if condition is true, df['scored'] = 1, if false, df['scored'] = 0
points_features = df.spatial.to_featurelayer('Points Features') # <- tripping point
edit_result = item1.edit_features(updates=points_features.features)
if 'success' in edit_result:
print(f"Data in {item1.title} has been updated successfully.")
else:
print("Edit features operation failed. Check the error message for details.")
print(edit_result['error'])
This bit has been troublesome, and I assume it has to do with the datatypes in the dataframe being incompatible with the datatypes for the feature layer.
The current error message:
ValueError: could not convert string to float: 'test@test.com'
This obviously makes sense, but the df field for email (which is never manipulated) is a "string[python]". Why is it trying to force it to be a float? I can't find any documentation or example on how to force field types or anything. Do I need to use "sanitize columns" and then overwrite the types?
The exact error:
ValueError Traceback (most recent call last)
Cell In[42], line 1
----> 1 points_features = scored_df.spatial.to_featurelayer('Scored Points')
2 edit_result = points_layer.edit_features(updates=points_features.features)
3 if 'success' in edit_result:
File /path/to/lib/python3.8/site-packages/arcgis/features/geo/_accessor.py:2846, in GeoAccessor.to_featurelayer(self, title, gis, tags, folder, sanitize_columns, service_name, **kwargs)
2838 if (
2839 content.is_service_name_available(service_name, "featureService")
2840 is False
2841 ):
2842 raise ValueError(
2843 "This service name is unavailable for Feature Service."
2844 )
-> 2846 result = content.import_data(
2847 self._data,
2848 folder=folder,
2849 title=title,
2850 tags=tags,
2851 sanitize_columns=sanitize_columns,
2852 service_name=service_name,
2853 **kwargs,
2854 )
2855 self._data.columns = origin_columns
2856 self._data.index = origin_index
File /path/to/lib/python3.8/site-packages/arcgis/gis/__init__.py:7569, in ContentManager.import_data(self, df, address_fields, folder, item_id, **kwargs)
7563 elif has_pyshp:
7564 name = "%s%s.shp" % (
7565 random.choice(string.ascii_lowercase),
7566 uuid4().hex[:5],
7567 )
-> 7569 ds = df.spatial.to_featureclass(
7570 location=os.path.join(temp_dir, name),
7571 sanitize_columns=sanitize_columns,
7572 )
7573 zip_shp = zipws(path=temp_dir, outfile=temp_zip, keep=False)
7574 item = self.add(
7575 item_properties={"title": title, "tags": tags},
7576 data=zip_shp,
7577 folder=folder,
7578 )
File /path/to/lib/python3.8/site-packages/arcgis/features/geo/_accessor.py:2637, in GeoAccessor.to_featureclass(self, location, overwrite, has_z, has_m, sanitize_columns)
2635 origin_columns = self._data.columns.tolist()
2636 origin_index = copy.deepcopy(self._data.index)
-> 2637 result = to_featureclass(
2638 self,
2639 location=location,
2640 overwrite=overwrite,
2641 has_z=has_z,
2642 sanitize_columns=sanitize_columns,
2643 has_m=has_m,
2644 )
2645 self._data.columns = origin_columns
2646 self._data.index = origin_index
File /path/to/lib/python3.8/site-packages/arcgis/features/geo/_io/fileops.py:1108, in to_featureclass(geo, location, overwrite, validate, sanitize_columns, has_m, has_z)
1106 return res
1107 else:
-> 1108 res = _pyshp2(df=df, out_path=out_location, out_name=fc_name)
1109 df.set_index(old_idx)
1110 return res
File /path/to/lib/python3.8/site-packages/arcgis/features/geo/_io/fileops.py:1336, in _pyshp2(df, out_path, out_name)
1334 if value is np.nan:
1335 row[idx] = None
-> 1336 shpfile.record(*row)
1337 del idx
1338 del row
File /path/to/lib/python3.8/site-packages/shapefile.py:2300, in Writer.record(self, *recordList, **recordDict)
2297 else:
2298 # Blank fields for empty record
2299 record = ["" for _ in range(fieldCount)]
-> 2300 self.__dbfRecord(record)
File /path/to/lib/python3.8/site-packages/shapefile.py:2335, in Writer.__dbfRecord(self, record)
2333 value = format(value, "d")[:size].rjust(size) # caps the size if exceeds the field size
2334 else:
-> 2335 value = float(value)
2336 value = format(value, ".%sf"%deci)[:size].rjust(size) # caps the size if exceeds the field size
2337 elif fieldType == "D":
2338 # date: 8 bytes - date stored as a string in the format YYYYMMDD.
ValueError: could not convert string to float: 'test@test.com'
EDIT:
I can get around this error, but always run into another error. For example, if I explicitly convert the GeoDF back to a SEDF via "scored_sedf = GeoAccessor.from_geodataframe(df, column_name="geometry")", I now get a "AttributeError: 'Series' object has no attribute 'type'" during to_featureclass().