|
POST
|
There are multiple ways to use for loops: var arry = [1, 2, 3]
// The "normal" way
// we define start value, end value, and increment of the loop variable
for(var i = 0; i < Count(array); i++) {
Console(array[i])
}
// The "for in" way
// This is a shorter form of the "normal" approach
// the loop variable is the index of the array
for(var i in array) {
Console(array[i])
}
// the "for in" way on a FeatureSet
// when you use "for in" on a feature set, the loop variable is not the index
// of the feature set, but the actual feature.
var fs = FeatureSetByName(...)
for(var f in fs) {
Console(f.Field)
} You tried to mix the second and third way. f is the index of fieldList, not the value at that index. Easiest way to do this is probably this: // get the _values_
var value_list = [
$feature.IssueAdjacentProperty,
$feature.IssueCultivation,
$feature.IssueFoodPlots,
$feature.IssueHaying,
$feature.IssueTimberHarvestCutting,
// ...
]
// define a filter function
function equals_search_value(val) { return val == "x" }
// filter the value array and return the count
return Count(Filter(value_list, equals_search_value))
... View more
04-07-2022
05:47 AM
|
1
|
3
|
8782
|
|
POST
|
Ah, the points are subtyped, got it. Well, you apparently already did the hard part of creating the sequences. The Attribute Rule itself is quite easy: // if we're updating this $feature, but not its subtype field, return
if($editcontext.editType == "UPDATE" && $feature.IntegerField == $originalfeature.IntegerField) {
return $feature.TextField
}
// get intersecting polygon
var polygons = FeatureSetByName($datastore, "TestPolygons", ["TextField"], true)
var poly = First(Intersects(polygons, $feature))
if(poly == null) { return null }
// get next sequence value based on polygon name and point subtype
// if that sequence doesn't exist, this will give an error!
var seq_name = poly.TextField + "_" + $feature.IntegerField
// get and format next sequence value
var seq_value = Text(NextSequenceValue(seq_name), "000")
// return the id with the order according to the point's subtype
if($feature.IntegerField == 1) {
return seq_value + poly.TextField
}
return poly.TextField + seq_value Not sure if having so many sequences will have a negative effect on the database performance. Me neither. The reason I suggested the approach in my previous post was the work of creating and maintaining the database sequences. The Rule in this post will fail if you create new polygons or new subtypes without also creating the corresponding database sequences:
... View more
04-07-2022
04:30 AM
|
1
|
0
|
3092
|
|
POST
|
create the fields "ActivityCode" and "ActivityDescription" in your table use the expressions below to calculate the fields (use Arcade as language) // Arcade expressions to calculate ActivityCode and ActivityDescription
// from Activity
// for Code
return Split($feature.Activity, "-")[0]
// for Description
return Split($feature.Activity, "-")[-1] if you want this to be one automatically for new or updated features, create an Attribute Rule on your table, use the expression below. // Calculation Attribute Rule on your feature class
// field: empy
// triggers: insert, update
var activity_split = Split($feature.Activity, "-")
return {
"result": {
"attributes": {
"AcivityCode": activity_split[0],
"AcivityDescription": activity_split[-1],
}
}
}
... View more
04-07-2022
01:36 AM
|
1
|
0
|
5183
|
|
POST
|
With so many sequences it will probably easier to use another approach: // get intersecting polygon
var polygons = FeatureSetByName($datastore, "gewaesserkataster_sde.GWK_SDE_ADMIN.TestPolygons", ["TextField", "IntegerField"], true)
var poly = First(Intersects(polygons, $feature))
if(poly == null) { return null }
// get all points that intersect the same polygon, load the sequence field
var points = FeatureSetByName($datastore, "gewaesserkataster_sde.GWK_SDE_ADMIN.TestPoints", ["TextField"], true)
points = Intersects(points, poly)
points = Filter(points, "TextField IS NOT NULL") // use only points with a sequence value
// get the next sequence count for this polygon (name and subtype)
// here, I chose "Name-Subtype-123" as schema
// edit the code according to your specification
var numbers = []
for(var p in points) {
Push(numbers, Split(p.TextField, "-")[-1])
}
// if numbers is empty, max(numbers) returns -infinity
// that's why we have to max that again, so that we get 1
// if this is the first point in the polygon
var next_number = Max(Max(numbers), 0) + 1
// return the new sequence value
// again, edit according to your specification
return Concatenate([poly.TextField, poly.IntegerField, next_number], "-") polygons.TextField corresponds to your MetroCode, polygons.IntegerField is the subtype field. points.TextField is the field that stores the sequence values in the point fc. Pros of this approach: easy for large numbers of needed sequences flexible: you don't have to change any code to add more polygons or subtypes Cons of this approach: will be slower if you have many polygons or points doesn't return unique ids. if you delete the point with the highest sequence number and create a new one, the new will have the sequence value of the old e.g.: delete F-3-2 and create a new point in F3. the new point will again be F-3-2; with a database sequence you would get F-3-3 Also many thanks for your contributions to the attribute rules community, almost every question I have you have a post with the solution. Thanks, glad to know that it helps people.
... View more
04-07-2022
12:40 AM
|
1
|
0
|
3104
|
|
POST
|
Hmmm... My first instinct would be to create an elliptical buffer, with the feature being in the ellipsis focal point that is pointed into the wind. def elliptic_buffer(in_features, id_field, major, minor, azimuth, out_features):
"""Creates an alliptic buffer around the input features.
in_features: input feature class / layer
id_field: name of a unique id field in the in_features
major: buffer distance in the major elliptical axis (meters)
minor: buffer distance in the minor elliptical axis (meters)
azimuth: geographic angle of the buffer (degrees, North = 0°, East = 90°)
out_features: output feature class
"""
arcpy.env.addOutputsToMap = False
# read shapes and id
shapes = [row for row in arcpy.da.SearchCursor(in_features, ["SHAPE@", id_field])]
sr = shapes[0][0].spatialReference
# create ellipsis parameters
ellipsis_table = arcpy.management.CreateTable("memory", "ellipsis_table")
fields = ["X", "Y", "MAJOR", "MINOR", "AZIMUTH", "ID"]
for f in fields:
arcpy.management.AddField(ellipsis_table, f, "DOUBLE")
with arcpy.da.InsertCursor(ellipsis_table, fields) as cursor:
for shape, id in shapes:
densified_shape = shape.densify("DISTANCE", 10, 10)
for part in densified_shape:
for point in part:
cursor.insertRow([point.X, point.Y, major, minor, azimuth, id])
# create ellipsis for each point of each densified feature (polyline)
ellipsis_fc = arcpy.management.TableToEllipse(ellipsis_table, "memory/ellipsis_fc", "X", "Y", "MAJOR", "MINOR", "METERS", "AZIMUTH", "DEGREES", "ID", sr)
# translate features, so that each determining point is in the focal point pointed into the wind
dist = (major**2 - minor**2)**(0.5)
dx = dist * math.sin(azimuth * math.pi / 180) / 2
dy = dist * math.cos(azimuth * math.pi / 180) / 2
with arcpy.da.UpdateCursor(ellipsis_fc, ["SHAPE@XY"]) as cursor:
for row in cursor:
cursor.updateRow([[row[0][0] - dx, row[0][1] - dy]])
# create output fc
arcpy.env.addOutputsToMap = True # add out_features to map
out_fc = arcpy.management.CreateFeatureclass(os.path.dirname(out_features), os.path.basename(out_features), "POLYGON", spatial_reference=sr)
id_type = [f.type for f in arcpy.ListFields(in_features) if f.name == id_field][0]
if id_type == "OID":
id_field = "FID"
id_type = "LONG"
arcpy.management.AddField(out_fc, id_field, id_type)
# convert ellipses to polygon (FeatureToPolygon doesn't keep the id_field...)
ellipses = [row for row in arcpy.da.SearchCursor(ellipsis_fc, ["ID", "SHAPE@"])]
ids = {e[0] for e in ellipses}
with arcpy.da.InsertCursor(out_fc, ["SHAPE@", id_field]) as i_cursor:
for id in ids:
polylines = [e[1] for e in ellipses if e[0] == id]
polygon = polylines[0].convexHull()
for polyline in polylines:
polygon = polygon.union(polyline.convexHull())
i_cursor.insertRow([polygon, id])
elliptic_buffer("TestPolygons", "OBJECTID", 1000, 500, 45, "memory/test") These are example buffers for a wind from the north east (azimuth = 45°): Using appropriate major and minor distances according to wind speed would then be a problem of experience or calibration. Generally speaking: low wind speed: major and minor distance are similar and relatively small high wind speed: major distance is much greater that minor distance Edit: For ArcMap, you have to replace all "memory" instances with "in_memory".
... View more
04-04-2022
06:48 AM
|
1
|
0
|
1398
|
|
POST
|
There isn't a dedicated function for that, but you can do it using a little math and array manipulation: // get the new distance of the polyline
// we should make sure it isn't zero.
var old_length = Length(Geometry($feature), "meters")
var new_length = Max(1, Round(old_length)) // 4.2 -> 4; 4.5 -> 5; 0.1 -> 1
// we want to change the geometry of the $feature, which means changing the
// paths attribute, where the points are stored.
// this is an array, and arrays can't be changed, so we have to copy all
// points except the last one (because we want to change that) into a new array.
var path = Geometry($feature).paths[0]
var new_path = []
for(var p = 0; p < Count(path)-1; p++) {
Push(new_path, [path[p].x, path[p].y])
}
// we only change the last segment of the polyline, so we have to get the
// new length and the angle of that segment
var last_length = Distance(path[-2], path[-1])
var new_last_length = last_length + (new_length - old_length)
var a = Angle(path[-2], path[-1]) * PI / 180 // degrees to radians
// now we can use simple trigonometry to calculate the new last point and
// append it to the array.
Push(new_path, [
new_path[-1][0] + new_last_length * Cos(a), // new_x = old_x + distance * cos(angle)
new_path[-1][1] + new_last_length * Sin(a) // new_y = old_x + distance * sin(angle)
])
// and then we convert the point array to a polyline and return that.
return Polyline({"paths": [new_path], "spatialReference": Geometry($feature).spatialReference}) Use this expression as a Calculation Attribute Rule on the Shape field, triggers: Insert and Update. This will change the last segment of the polyline to make the polyline an integer length. Well, almost: there seem to be some rounding errors...
... View more
04-03-2022
11:15 PM
|
1
|
0
|
1940
|
|
IDEA
|
Hmmm... I see the need for the user to know what happens if they edit a feature class. First of all, I think that is your responsibility as database admin or data owner. If you grant write privileges to a user, you better make sure they understand the data structures and edit workflows they are dealing with. But a message to remind the user or to control that the rules work correctly couldn't hurt. I see two ways that could be implemented: ArcGIS notifies the user automatically when a table gets edited due to an Attribute Rule. That would be bad for these reasons: In some cases, you get other feedback and don't need the message. eg. I insert a point, snap it to a line and split the line on that point. I can see the result on the screen, the message is unneeded ballast. Some Attribute Rules edit many tables (up to 5 in my case). I would hate to have that many messages pop up every time I make an edit. If this is a workflow I do often, or if I edit many features during an edit session, then I know what happens. After I saw the message a few times, I know. At that point, it's completely unnecessary and just annoying. You define it in the rule, similar to the errorMessage keyword: var value = 5
return {
"edit": [{
"className": "OtherClass",
"adds": [
{"attributes": {"Field": value}}
]
}],
"successMessage": "We did it! 1 feature created in OtherClass."
} Technically, you would be lying to the user, because at that point the edits wouldn't be applied yet, but that's the whole philosophy behind positive UX design... This method would be good, because It is customizable. Instead of a generic "Feature added to Class X", you can return a specific message. This also means you can tell the user that multiple classes were edited in one message. It's optional. For edit workflows that you expect to do often, you just don't return a message, because you know what happens. Personally, I wouldn't want the message to show up on the top of the map, like in your mockup, but in the Create Features Pane / Modify Features Pane. The generic success messages and error messages are shown there already, so imo that's the right place for custom messages.
... View more
03-31-2022
12:56 AM
|
0
|
0
|
3583
|
|
POST
|
Oops, I didn't change all of the variable names... Fixed it in my answer.
... View more
03-30-2022
10:40 PM
|
0
|
0
|
3785
|
|
POST
|
// load the point fc
var points = FeatureSetByName($datastore, "NameOfPointFC", ["GlobalID"], true)
// intersect with current line
var intersecting_points = Intersects($feature, points)
// loop through all intersecting points
// return the GlobalID of the first point that lies on the $feature's end point
var feature_end = Geometry($feature).paths[0][-1]
for(var p in intersecting_points) {
if(Intersects(feature_end, p)) {
return p.GlobalID
}
}
// if we land here, there is no point on the $feature's end point
return null
... View more
03-30-2022
12:57 AM
|
1
|
1
|
1822
|
|
POST
|
I have no clue about SWEET and if this will work, but you're looking for the Push function. var TestingPolicies = FeatureSetByName($map,"Testingpolicy")
// if you have multiples of each DCA_Type in that table, you might want to use Distinct:
//TestingPolicies = Distinct(TestingPolicies, "DCA_Type")
var options = []//create empty list
for (var record in TestingPolicies){
var option = record.DCA_Type
Push(options, option)//add each option to the list
}
return options
... View more
03-30-2022
12:24 AM
|
0
|
1
|
2529
|
|
POST
|
Then you use a logical AND to concatenate the conditions (A >= 1 AND A <= 75000) return IIf($feature.A >= 1 && $feature.A <= 75000, "Yes", "No")
... View more
03-26-2022
04:02 AM
|
1
|
0
|
2054
|
|
POST
|
Make sure that the coordinate systems of your different maps are the same (and ideally match the coordinate system of your feature classes :
... View more
03-25-2022
03:07 AM
|
1
|
0
|
6263
|
|
POST
|
As the others have said, you can't iterate an InsertCursor. You want to update existing features in class B, so you need an UpdateCursor. Starting from line 17 in your code: with arcpy.da.UpdateCursor(B, [idfieldB, 'SHAPE@']) as cursor:
for key, shape in cursor: #iterate through the existing features in B
try:
new_shape = geometries[key]
cursor.updateRow([key, new_shape])
except KeyError:
notfound.append(key)
... View more
03-25-2022
01:18 AM
|
2
|
3
|
3851
|
|
POST
|
// Calculation Attribute Rule on your feature class
// field: B
// triggers: insert, update
return IIf($feature.A <= 75000, "Yes", "No") // if condition is true, then "Yes", else "No" The ArcGIS Developer page for Arcade is a good place to start: Getting Started | ArcGIS Arcade | ArcGIS Developer I find the function reference especially important, this is a page that I have bookmarked: Function Reference | ArcGIS Arcade | ArcGIS Developer If you want to play around, you can do so in the Arcade Playground: Playground | ArcGIS Arcade | ArcGIS Developer If you're looking for examples, there is a Github with Arcade expressions for diffferent profiles: GitHub - Esri/arcade-expressions: ArcGIS Arcade expression templates for all supported profiles in t...
... View more
03-25-2022
01:07 AM
|
1
|
2
|
2075
|
|
POST
|
Why exactly doesn't an Attribute Rule work here, though? Brian first asked this question in the ArcGIS Pro SDK community, where I told him that. My reason for that was that some of the users work in ArcMap, which doesn't like Attribute Rules. I don't know about FME, webservices and "other front-end apps" probably depends. If there was a way to get Attribute Rules to work reliably in all those systems, they would of cource be the goto choice for something this simple. // Calculation Attribute Rule
// field: MXCREATIONSTATE
// triggers: insert, update
if($editcontext.editType == "INSERT") {
return 1
}
if($feature.MXCREATIONSTATE != $originalfeature.MXCREATIONSTATE) {
return $feature.MXCREATIONSTATE
}
return 1
... View more
03-25-2022
12:12 AM
|
1
|
1
|
5152
|
| 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
|