|
POST
|
Having the same logic in multiple rules is inefficient and can lead to errors quickly, when you change one rule and forget to update the others. In this case, It could also quickly lead to the cycling error again. It's better to expand the rule from your previous question. In lines 12-18, we now also check if the ParentPropertyID changed. If neither the geometry nor the parent id changed, we abort. In lines 45-57, we now also update all features with the previous ParentPropertyID (if it changed), because their total area is now smaller. // Calculation Attribute Rule
// field: empty
// triggers: Insert, Update, Delete
// exclude from application evaluation
var mode = $editcontext.editType
var geometry_is_equal = true
var parent_id_is_equal = true
// abort if this is an update that doesn't change the geometry or the ParentPropertyID
if(mode == "UPDATE") {
geometry_is_equal = Equals(Geometry($feature), Geometry($originalfeature))
parent_id_is_equal = $feature.ParentPropertyID == $originalfeature.ParentPropertyID
if(geometry_is_equal && parent_id_is_equal) {
return
}
}
// get all OTHER features with this ID
var id = $feature.ParentPropertyID
var gid = $feature.GlobalID
var query = "ParentPropertyID = @ID AND GlobalID <> @gid"
var fs = Filter($featureset, query)
// get this area and the sum of the other areas
var f_area = Area($feature, "acres")
var total_area = Sum(fs, "GIS_TractAcres")
if(mode != "DELETE") { total_area += f_area }
// construct the update array
var updates = []
// update all features with the same ParentPropertyID
for(var f in fs) {
if(f.GlobalID == $feature.GlobalID) { continue }
var u = {
globalID: f.GlobalID,
attributes: {
GIS_PropertyAcres: total_area
}
}
Push(updates, u)
}
// update all features with the previous ParentPropertyID
if(!parent_id_is_equal) {
var prev_id = $originalfeature.ParentPropertyID
var prev_fs = Filter($featureset, "ParentPropertyID = @prev_id")
for(var f in prev_fs) {
var u = {
globalID: f.GlobalID,
attributes: {
GIS_PropertyAcres: f.GIS_PropertyAcres - f_area
}
}
Push(updates, u)
}
}
return {
result: {
attributes: {GIS_TractAcres: f_area, GIS_PropertyAcres: total_area}
},
edit: [{
className: "TestPolygons",
updates: updates
}]
} You might have to refresh the attribute table to see the changes.
... View more
07-27-2023
07:29 AM
|
0
|
0
|
1531
|
|
POST
|
You shouldn't have to manually add the Edit tab, it should appear automatically. Ribbon tabs are context-sensitive: they change depending on your active view (e.g. catalog, map, layout) and selection (e.g. select a raster layer vs a feature layer in a map's toc). The Edit tab should show up when you open a map. For example, here I am in the Catalog View. The ribbon displays the "Catalog" tab with catalog-related tools. This tab only shows up in the Catalog view. When I open the map, the ribbon switches the tabs to the ones appropriate for maps, including the "Edit" tab:
... View more
07-27-2023
05:47 AM
|
1
|
1
|
4176
|
|
POST
|
To post formatted code: SearchCursor reads values from date fields as datetime.datetime objects, so you can compare to that. import arcpy
import datetime
import pandas as pd
def analyze(in_table):
fields = arcpy.ListFields(in_table)
result = [
{"Field": f.name, "Type": f.type, "Nulls": 0, "Zeroes": 0, "Unknowns": 0, "DefaultDate": 0}
for f in fields
]
with arcpy.da.SearchCursor(in_table, [f.name for f in fields]) as cursor:
for row in cursor:
for i, val in enumerate(row):
if val is None:
result[i]["Nulls"] += 1
elif val == 0:
result[i]["Zeroes"] += 1
elif val == "unknown":
result[i]["Unknowns"] += 1
elif result[i]["Type"] == "Date" and val <= datetime.datetime(1950, 12, 29):
result[i]["DefaultDate"] += 1
df = pd.DataFrame(result)
return df
display(analyze("G:/ArcGIS/data/Arcade_Test_Data.gdb/TestPoints"))
... View more
07-27-2023
04:32 AM
|
0
|
1
|
2417
|
|
POST
|
Inline variables are meant to replace parameter input in the tool's GUI, not in its code. To get a parameter value inside the tool's code, use the GetParameter functions. print() won't show anything inside a tool, as the tool doesn't use the Python Window as output. Use AddMessage instead to output into the tool messages. import arcpy
if __name__ == "__main__":
in_table = arcpy.GetParameter(0)
for f in arcpy.ListFields(in_table):
arcpy.AddMessage([f.name, f.type]) And then you can easily add it to a model: If you want something more permanent than a simple print output, you can use this script as a script tool. It will iterate through each table and feature class in a workspace and store the field information in a new table. # parameter 1: Workspace, input
# parameter 2: Table, output
import arcpy
from pathlib import Path
in_ws = arcpy.GetParameterAsText(0)
out_table = arcpy.GetParameterAsText(1)
# create output table
folder = str(Path(out_table).parent)
name = Path(out_table).stem
out_table = arcpy.management.CreateTable(folder, name)
arcpy.management.AddField(out_table, "TableName", "TEXT")
arcpy.management.AddField(out_table, "FieldName", "TEXT")
arcpy.management.AddField(out_table, "FieldType", "TEXT")
# start writing
with arcpy.da.InsertCursor(out_table, ["TableName", "FieldName", "FieldType"]) as cursor:
# iterate through the workspace's feature classes and tables
walk = arcpy.da.Walk(in_ws, datatype=["FeatureClass","Table"])
for dir_path, dir_names, file_names in walk:
for file_name in file_names:
in_table = str(Path(dir_path) / file_name)
# get the fields
fields = arcpy.ListFields(in_table)
# write the fields into the output table
for f in fields:
row = [in_table, f.name, f.type]
cursor.insertRow(row)
# set the output parameter
arcpy.SetParameter(1, out_table)
... View more
07-26-2023
12:23 PM
|
0
|
2
|
1978
|
|
POST
|
Here is the documentation of the Arcade Dashboard Data profile: https://developers.arcgis.com/arcade/profiles/dashboard-data/ From there: Profile variables This profile does not provide any profile variables. So you don't have access to the profile variables you are used to from other profiles, like $map, $datastore, or $feature. Instead of FeaturesetByName, use FeaturesetByPortalItem.
... View more
07-26-2023
02:33 AM
|
0
|
1
|
2228
|
|
POST
|
You can filter out null values, then the legend item shouldn't be there anymore.
... View more
07-25-2023
11:24 AM
|
1
|
1
|
1756
|
|
POST
|
You have to get the max of the filtered featureset. var GRID_ID_SELECT = AA
var GridFilter = Filter($featureset, "GRID_ID = @GRID_ID_SELECT")
var NEW_ID = Max(GridFilter, "ID") + 1
return NEW_ID
... View more
07-25-2023
11:19 AM
|
0
|
1
|
1138
|
|
POST
|
There are tools in the Spatial Analyst toolbox: https://pro.arcgis.com/en/pro-app/latest/tool-reference/spatial-analyst/an-overview-of-the-hydrology-tools.htm And there ist the Arc Hydro solution with advanced pre and postprocessing tools: https://www.esri.com/en-us/industries/water-resources/arc-hydro
... View more
07-25-2023
02:09 AM
|
0
|
1
|
1570
|
|
POST
|
I can't find any documentation for an official tool called "MakeSamplePointGrid". Was this a custom tool? What does the tool do?
... View more
07-24-2023
04:34 PM
|
0
|
2
|
1546
|
|
POST
|
Hmm. For me, the widget returns slightly larger values, but that's because it only displays two significant digits and seems to always round up. It might be the coordinate system, but I have absolutely no experience working in State Plane, so I can't say anything about it. If your polygons cover great distances, you might want to use DistanceGeodetic to account for Earth's curvature. Make sure that your polygons are inside your state zone. Also, make sure that the code and the measure widget use the same distance method (planar or geodesic), and that you actually snap to the polygon's vertices. All the Arcade Geometry functions warn about generalization errors when using them for labeling or symbology: Feature geometries in the visualization and labeling profiles are generalized according to the view's scale resolution to improve drawing performance. Therefore, using a feature's geometry (i.e. $feature) as input to any geometry function in these contexts will return different results at each scale level. Other profiles, such as popup, provide the full resolution geometry. I haven't seen this effect yet, but I expect it would show up for polygons with many vertices. If none of these help, I suggest doing the distance calculation manually and comparing it to both measuring widget and code. At least then you know which one is correct.
... View more
07-24-2023
04:26 PM
|
0
|
2
|
5116
|
|
POST
|
Hmmm, this works, but I really hope I missed an easier solution. # extract your watershed from the larger digital elevation model
ws_dem = arcpy.sa.ExtractByMask("DEM", "Cutter")
# resample to 100x100
ws_dem_resampled = arcpy.management.Resample(ws_dem, "ws_dem_resampled", "100 100")
# fill and flow dir
ws_dem_filled = arcpy.sa.Fill(ws_dem_resampled)
flow_dir = arcpy.sa.FlowDirection(ws_dem_filled)
# create an id raster (numbered sequentially)
cell_points = arcpy.conversion.RasterToPoint(ws_dem_filled, "cell_points", "Value")
id_raster = arcpy.Raster(arcpy.conversion.PointToRaster(cell_points, "pointid", "id_raster", cellsize=flow_dir))
# calculate downstream id
directions = {
1: (1, 0),
2: (1, 1),
4: (0, 1),
8: (-1, 1),
16: (-1, 0),
32: (-1, -1),
64: (0, -1),
128: (1, -1)
}
ds_id_raster = arcpy.Raster(id_raster.getRasterInfo())
for x, y in ds_id_raster:
d = flow_dir[x, y]
i = id_raster[x,y ]
try:
dy, dx = directions[d]
i_ = id_raster[x+dx, y+dy]
except:
continue
ds_id_raster[x, y] = i_
ds_id_raster.save("ds_id_raster")
# convert raster to polygons
cells = arcpy.conversion.RasterToPolygon(id_raster, "Cells", "NO_SIMPLIFY")
# extract values into the points and join
arcpy.sa.ExtractMultiValuesToPoints(cell_points, [(id_raster, "Cell"), (ds_id_raster, "CellDS"), (flow_dir, "FlowDir"), (ws_dem_filled, "Elevation")])
arcpy.management.JoinField(cells, "gridcode", cell_points, "pointid", ["Cell", "CellDS", "FlowDir", "Elevation"]) This script will: resample your dem calculate flow direction Give a unique id to each raster cell Use the flow direction to get the downstream cell id Extract all this information into a polygon grid
... View more
07-24-2023
10:40 AM
|
1
|
0
|
1330
|
|
POST
|
The second argument of Includes() has to be a single value, you're trying to input an array. https://developers.arcgis.com/arcade/function-reference/array_functions/#includes
... View more
07-24-2023
03:48 AM
|
1
|
0
|
1702
|
|
POST
|
When you return a dictionary with update instructions, these updates trigger the rule again. You have it set up so that the same feature gets updated over and over and over, so at some point ArcGIS halts the loop and errors out. I think you have the right idea in line 11: You need to check whether the attribute you care about was changed by the update. If not, you don't need to return further update instructions. I'm not really sure why your condition isn't working, and I honestly don't feel like diving too deep into it... Anyway, here is an expression that should work as expected. It updates both GIS_TractAcres and GIS_PropertyAcres, so you only need this one rule. It also handles deleting features properly. // Calculation Attribute Rule
// field: empty
// triggers: Insert, Update, Delete
// exclude from application evaluation
var mode = $editcontext.editType
// abort if this is an update that doesn't change the geometry
if(mode == "UPDATE" && Equals(Geometry($feature), Geometry($originalfeature))) {
return
}
// get all OTHER features with this ID
var id = $feature.ParentPropertyID
var gid = $feature.GlobalID
var query = "ParentPropertyID = @ID AND GlobalID <> @gid"
var fs = Filter($featureset, query)
// get this area and the sum of the other areas
var f_area = Area($feature, "acres")
var total_area = Sum(fs, "GIS_TractAcres")
// if this is an insert or update, add this area to the total (don't do it if this is a delete)
if(mode != "DELETE") {
total_area += f_area
}
// construct the update array
var updates = []
for(var f in fs) {
if(f.GlobalID == $feature.GlobalID) { continue }
var u = {
globalID: f.GlobalID,
attributes: {
GIS_PropertyAcres: total_area
}
}
Push(updates, u)
}
return {
result: {
attributes: {
GIS_TractAcres: f_area,
GIS_PropertyAcres: total_area}
},
edit: [{
className: "Arcade_Rules_Test",
updates: updates
}]
}
... View more
07-21-2023
03:39 PM
|
0
|
0
|
1708
|
|
POST
|
unhashable type: 'list'. You get this error because I used the *args declaration in get_mode. The asterisk is the unpacking operator. It is used here to allow any number of arguments and work with them as tuple inside the function. You are giving the fields as list, not as separate arguments. That means that the function calls statistics.mode( ( [1, 2, 3], ) ) , not statistics.mode( [1, 2, 3] ), it inputs the list as one complete value to check. statistics.mode uses a hashmap/dictionary internally to count the elements So it tries to use the list as key for that hashmap. But lists are mutable, so they can't be hashed, thus the error. So the first step to fixing this is fixing the code block (just remove the asterisk): codeblock = """
def Get_mode(args):
return statistics.mode(args)
""" You could also skip the code block altogether and just call statistics.mode directly in the expression field. The next step is to fix your input list. Right now you're inputting string values. Assuming you have fields like "R1", "R2" and so on, you're doing this: field_names = ["R1", "R2", "R3", "R4"]
statistics.mode(field_names)
# "R1" You need to input the actual values. In the field calculator, you do this by enclosing the field name in exclamation points: field_names = ["!R1!", "!R2!", "!R3!", "!R4!"] But that still won't work, because that will input this literal list and just return "!R1!" for each row. Instead, you need to completely construct the whole expression instead of relying on an outside variable: # get the field names
field_names = [f.name for f in arcpy.ListFields(inTable, "R*")] # ['R1', 'R2', 'R3']
# surround them in exclamation marks
field_names = [f"!{n}!" for n in field_names] # ['!R1!', '!R2!', '!R3!']
# concatenate
field_names = ", ".join(field_names) # '!R1!, !R2!, !R3!'
# get the function call as string
expression = f"Get_mode([{field_names}])" # 'Get_mode([!R1!, !R2!, !R3!])'
# alternatively: skip the code block and call statistics.mode directly:
expression = f"statistics.mode([{field_names}])" # 'statistics.mode([!R1!, !R2!, !R3!])'
# and finally execute the FIeld Calculator
arcpy.management.CalculateField(inTable, fieldName2, expression, "PYTHON3", codeblock) Personally, I don't like calling Calculate Field from a script. It's a tool for quick manual calculations, not for automating tasks. In your case, I'd just do it with an UpdateCursor: import arcpy
import statistics
arcpy.env.workspace = "my path"
mode_field = "Most_Freq"
for fc in arcpy.ListFeatureClasses():
arcpy.management.AddField(fc, mode_field, "LONG")
value_fields = [f.name for f in arcpy.ListFields(fc, "R*")]
with arcpy.da.UpdateCursor(fc, [mode_field] + value_fields) as cursor:
for row in cursor:
row[0] = statistics.mode(row[1:])
cursor.updateRow(row)
... View more
07-21-2023
02:48 PM
|
1
|
1
|
4098
|
|
POST
|
You mixed up the arguments of Includes(). First comes the array, then the element you search for. // array of OS_2 values for which you want to return the key
var os2_values = [33100, ]
// if the OS_2 value of the current $feature is included in that array, construct the key
if (Includes(os2_values, $feature.OS_2]) {
return $feature.STRSCHL+'_'+$feature.HAUSNR
}
// else just return what's in the field
return $feature.Key
... View more
07-21-2023
07:05 AM
|
1
|
2
|
1719
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 01-30-2023 09:57 AM | |
| 1 | 05-18-2023 12:51 AM | |
| 1 | 03-05-2023 12:46 PM | |
| 1 | 12-07-2022 07:01 AM | |
| 1 | 06-21-2022 08:27 AM |
| Online Status |
Offline
|
| Date Last Visited |
02-03-2024
06:14 PM
|