POST
|
I'm experiencing an issue where lines following a single "//" line are causing Attribute Rules to throw errors when trying to save them. The code below is essentially meaningless, but I can reproduce this exact issue in much larger scripts. This will not save, and throws the error shown (Object not found feat, Script line: 2). //
var feat = $feature
return feat This, however, saves as expected without any issues: //
var feat = $feature
return feat Seems like in the first example, that commented out first "//" line is ALSO consuming/commenting out the variable assignment on the following Line 2, and therefore the variable object is not found. I understand there isn't necessarily a good reason to use "//" to comment out a blank line, but I'm curious if this is expected behavior for some reason. Why would adding in an extra blank line make the difference? Thanks!
... View more
Wednesday
|
1
|
0
|
91
|
POST
|
It appears the fix for this was simply checking "Exclude From Client Evaluation" box within the Attribute Rules interface for this particular rule. I still need to read through the post here (entitled Attribute Rules - Exclude from Application Evaluation) to fully understand why that is though. Attribute Rules - Exclude from Application Evaluat... - Esri Community
... View more
2 weeks ago
|
0
|
0
|
85
|
POST
|
I am trying to write an Attribute Rule that calculates the value in FieldA over to the value in FieldB. Both fields have the same Domain applied (domain named "TwoOptions"), and that Domain has two codes: {OPT1: Option1, OPT2: Option2}. Here is the Field View, followed by the Domain. Code below is a bit over-engineered because it's from a general template I try to follow. // INPUTS
var inputFldVal = $feature.FieldA
// OUTPUTS
var outputFldName = "FieldB"
//---------------------------------------------------------------------//
// FUNCTIONS
function directCopyCalculation(inputFldVal) {
/* If value exists in input column, copy to results dictionary for output column.*/
var result = Dictionary("VALUE", null, "ERROR", null)
// CHECK 1: Value in origin field exists.
if (IsEmpty(inputFldVal))
{return result}
result["VALUE"] = inputFldVal
return result
}
//---------------------------------------------------------------------//
// MAIN
var fldUpdates = Dictionary()
var result = directCopyCalculation(inputFldVal)
if (!IsEmpty(result["ERROR"]))
// If any errors were found, return dict without any field updates.
{return {"errorMessage": `ERROR: ${result}`}}
else
// If no errors, update the results dict with the value for the new field.
{fldUpdates[outputFldName] = result["VALUE"]}
Console(`RETURNING: ${{"result": {"attributes": fldUpdates}}}`)
return {"result": {"attributes": fldUpdates}} It works fine when selecting <Null> or either of the domain options in FieldA. Trying to update FieldB results in FieldB automatically updating back to whatever the value in FieldA is, which is fine and expected. If I intentionally break the Domain in FieldA using Field Calculator to populate an invalid value, that value also calculates over to FieldB. This is also fine, and what I would expect. The problem arises if I try to manually change FieldA back to a valid value. I get the following error. Hitting "OK" reverts FieldA's value back to "VALUE OUTSIDE DOMAIN". Seems like FieldB is being validated for allowable domain values BEFORE the newly updated valid value from FieldA is calculated over to FieldB. Am I missing an order-of-calculations thing here? Something else?
... View more
2 weeks ago
|
0
|
1
|
111
|
POST
|
I have a polygon feature class related to a table via Relationship Class (one-to-many, GlobalID in polygon related to a GUID in the table, field called REL_ZONE_ID). I am updating this related table manually. The related table lists the segments that intersect the polygons (polygon labels shown here are a shorthand version of the full Globals, full version listed in the REL_ZONE_ID field). So basically it's a table that keeps track of the lines intersecting a given polygon. I am moving between Pro 3.1 and 3.2, so I can't yet reliably use "FeatureSetByRelationshipClass". The table shown is the related table, storing a record of each line that intersects a polygon. I would like to do this automatically with a Calculation Attribute Rule, at INSERT, UPDATE, and DELETE. My approach: Get a FeatureSet of the records that are already related to the original polygon. Mark them for deletion by adding to a "delete" array. Review all line segments (time consuming?); if any intersect the newly updated polygon, append segment ID record to a new "adds" array. Return an "edit" record to delete the originally related records, and add the newly intersected records. The below does this, but it's a hack--I needed to add in the if (editType != "DELETE") because otherwise when a polygon was deleted, the rule would remove the currently related records as expected, but then add the same records back in (except with a NULL related ID, since the polygon would have been deleted, and not have an ID anymore). I thought I wouldn't need to do the if DELETE because I thought the "Intersect()" was being called on the $feature, and not the $originalFeature. So I assumed that if a feature was deleted, there couldn't be any intersections with the $feature, and therefore no records would be added to the "adds" array. To reiterate, the code below does exactly what I want, I just can't do it without the special "DELETE" caveat. /*MANAGE RELATED SEGMENTS*/
//---------------------------------------------------------------------//
// INPUTS
var proVersion = 3.1
var editType = $editContext.editType
// Get original/current polygon feature.
var zone = $originalFeature
var newZone = $feature
// Get all polylines in SEGMENTS feature class. Geometry required for 'Intersects()'.
var segmentsFS = FeatureSetByName($datastore, "SEGMENT", ["SEG_ID"], true)
if (proVersion >= 3.2) {
// Get table records that are related to the current POLYGON feature.
var relRecordsFS = FeatureSetByRelationshipClass(zone, "ZONE_SEG_RELATION", ["*"], false)
} else {
// Get all records from the related table.
// Filter when related ID matches GlobalID of orignal POLYGON feature.
var relRecsFS = FeatureSetByName($datastore, "SEG_TBL", ["*"], false)
var zoneGlobalID = $originalFeature.GlobalID
var relRecordsFS = Filter(relRecsFS, 'REL_ZONE_ID = @zoneGlobalID')
}
// MAIN ------------------------------------------------------------------------------- #
// Iterate over the currently related table records, add all 'deletes' array.
var deletes = []
for (var relatedRecord in relRecordsFS) {
Console(`ALREADY RELATED / TO DELETE: ${relatedRecord.SEG_ID} | ${relatedRecord.GlobalID}`)
Push(deletes, {"globalID": relatedRecord.GlobalID})
}
// For every SEGMENT line, check if it intersects current ZONE. If so, add to 'adds' array.
var adds = []
if (editType != "DELETE") { // If a feature is deleted, there won't be anything getting added. "newZone" alone isn't working?
for (var seg in segmentsFS) {
if (Intersects(newZone, seg)) {
Console(`FOUND NEW INTERSECTION / TO ADD: ${seg.SEG_ID}`)
Push(adds, {"attributes": {"REL_ZONE_ID": newZone.GlobalID, "SEG_ID": seg.SEG_ID} })
}
}
}
Console(`\nDELETE RECORDS:\n${deletes}\n\nADD RECORDS:\n${adds}`)
return {
'edit': [{
'className': 'SEG_TBL', // This is the TABLE where the edits are occurring.
'deletes': deletes, // [{'globalID': XXX}, {'globalID': YYY}] <-- 2 delete examples
'adds': adds // [{"attributes": {"REL_ZONE_ID": newZone.GlobalID, "SEG_ID": seg.SEG_ID}}, {"attr...] <-- 2 adds
}]
}
... View more
04-04-2024
02:49 PM
|
0
|
0
|
298
|
POST
|
Hey @DrewFlater, a follow up to this. I just tried to use arcpy's AddField with a "BIGINTEGER" data type, and ran into a similar issue as above: ERROR 000800: The value is not a member of TEXT | FLOAT | DOUBLE | SHORT | LONG | DATE | BLOB | RASTER | GUID.
Failed to execute (AddField). I'm not going to open a Pro Idea because... this issue of new data types clearly hasn't been addressed in a lot of places, so the "idea" would extremely general. I'll leave the prioritization decisions to you all. Thanks again for the initial reply.
... View more
03-29-2024
01:30 PM
|
1
|
0
|
356
|
POST
|
Fair enough, I appreciate the insights. I'll go ahead and play around with replacing the string in FeatureSetByName. Thanks again!
... View more
03-27-2024
07:44 AM
|
0
|
1
|
379
|
POST
|
Thanks for the inputs @RPGIS, but the code I have provided already "works"--even without specifying this line below. Maybe that's because the Arcade "environment" knows that all feature classes/tables will have an ObjectID, so you don't need to specify? Regardless, it seems inconsistent. But I don't have it listed in my version, and things work as expected. var OID = $feature.OBJECTID My issue is that I don't want to have to specify the field name three different times in the same code. I'm not saying Python is without problems, but coming from Python there would never be a case where I would have to hard-code a string three different times in the same extremely small block of code. I would assign the string field name to a variable, and then use that variable in every instance where it is needed--if the field name changes, I change the single instance of that variable assignment, and it updates everywhere. In my example, and both of your examples, we are specifying the same field name "Name_Text" three different times in the same tiny script. If this is a bug, fine, but if this is how the language was designed... why?
... View more
03-26-2024
01:31 PM
|
0
|
3
|
413
|
POST
|
I am trying to write a Calculation style Attribute Rule to restrict entries into a field to be unique values within the field. As an example, more than one row in the given table can't have a value of "TEST" in the given field--entries must be unique. The below achieves this (might not handle NULLs, but I can sort that out). What is constantly irritating to me is how I cannot figure out how to use the variables defined on the first few lines in the "FeatureSetByName()" and "Expects()" function calls? Updating the FeatureSetByName() call with the fcName and fldName variables will work when I hit "OK" in the AR code editor, but then fail when I try to save the rule. Updating Expects() to use the fldName variable throws an error right in the editor. Why do these functions work this way? If I change the name of my field, I have to go in and retype it 3 different times in the code? Why can I not just use one single variable for those names? Is this some Arcade/JavaScript idiosyncrasy I just don't understand? Thanks in advance to anyone who can sort me out on this. The point is to create a rule that can be used in different locations, with minimal updates to only the INPUTS section. Esri's example (link in the code below) hard-codes things, which is silly from a reusability standpoint. // INPUTS
var fcName = "TEST";
var fldName = "Name_Text";
// These two lines must be written with the actual strings, not the variables above.
var features = FeatureSetByName($datastore, "TEST", ["Name_Text"], false);
Expects($feature, "Name_Text");
// MAIN
// If any values in the field match the current row's value (and the IDs are different),
// this is a duplicate, and an error.
// Largely copied from:
// https://support.esri.com/en-us/knowledge-base/how-to-identify-a-duplicate-field-value-using-an-attrib-000029088
for (var i in features) {
if ((i[fldName] == $feature[fldName]) && (i.OBJECTID != $feature.OBJECTID)) {
return {"errorMessage": "ERROR: value not unique within field!"}
}
}
// If error above not found, return results dict with value as-is.
var fldUpdates = Dictionary()
fldUpdates[fldName] = $feature[fldName]
Console({"result": {"attributes": fldUpdates}})
return {"result": {"attributes": fldUpdates}}
... View more
03-26-2024
12:08 PM
|
0
|
5
|
439
|
POST
|
Well at least the problem is repeatable. Thanks for testing.
... View more
03-21-2024
05:30 PM
|
0
|
0
|
470
|
POST
|
Anyone have any idea why you can sort a table based on a DATE field, but not a DATE ONLY field? I have both field types in the example feature class below, but when it comes to the Sort tool, only the DATE field is an option. Simple oversight by Esri, or am I missing something?
... View more
03-21-2024
03:12 PM
|
2
|
5
|
496
|
POST
|
Appreciate the input, thanks! This option will have to do for now. Here's what my implementation looks like: HHMM is a TEXT field that the user types in, HH is a FLOAT field that gets calculated. Figured reading HH would be more intuitive to understand than MM, but the concept is the same. I try to use the same "template" for Attribute Rules, so this is probably overly verbose, but here's the automatic calculation from HH:MM text to HH float: /*
Converts a text HH:MM format field and converts it to a float HH field.
*/
//---------------------------------------------------------------------//
// FUNCTIONS
function calculateHH(hhmm) {
// Set up results dict. If all checks pass, return this as-is.
var result = Dictionary("VALUE", null, "ERROR", null)
if (IsEmpty(hhmm)) {
return result
}
Console(hhmm)
var timeArr = Split(hhmm, ":")
var hh = Number(timeArr[0]) + (Number(timeArr[1]) / 60)
Console(`${hhmm} --> ${hh}`)
result["VALUE"] = Round(hh, 2)
return result
}
//---------------------------------------------------------------------//
// INPUTS
var hhmm = $feature.Hiking_HHMM
//---------------------------------------------------------------------//
// MAIN
var fldUpdates = Dictionary()
// Convert HHMM text to MM float ----------------
var result = calculateHH(hhmm)
if (!IsEmpty(result["ERROR"]))
{return {"errorMessage": `ERROR: ${result}`}}
else
{fldUpdates["Hiking_HH"] = result["VALUE"]}
Console({"result": {"attributes": fldUpdates}})
return {"result": {"attributes": fldUpdates}} And my solution to verifying the initial HH:MM inputs: /*
Script provides validation for a TEXT field that should contain a time duration
in the format H?H?:MM. There are several checks, since RegEx is not available.
1) Blank strings are not allowed; null values are fine
2) Format must contain a colon--as in 3:30, 12:04, :37
3) Values on either side of the colon must be numeric
4) Hours are optional, but must be 0 - 23
5) Minutes must always be two characters, and numeric values 0 - 59
This script is meant to be used as Calculation type AR, rather than a
Constaint AR.
*/
//---------------------------------------------------------------------//
// FUNCTIONS
function timeValidate (time) {
// Set up results dict. If all checks pass, return this as-is.
var result = Dictionary("VALUE", time, "ERROR", null)
// First check empty, then null. Can't do both simultaneously nicely.
if ((TypeOf(time) == "String") && Trim(time) == "") {
result["ERROR"] = "Blank input is not allowed."
return result
}
else if (IsEmpty(time)) {
return result
}
// Console(timeArray, Count(timeArray));
// Must contain one ":", yields two elements with Split().
if (Count(timeArray) != 2) {
result["ERROR"] = "Format must contain one colon."
return result
}
// Check if both values in the array are numbers.
for (var i in timeArray) {
var tPart = timeArray[i]
Console(`PART: ${i}, VAL: ${tPart}, ASNUM: ${Number(tPart)}, ISNAN: ${IsNan(Number(tPart))}`)
// If Number returns NaN, "Casting a non-numeric text or undefined to a number".
if (IsNan(Number(tPart))) {
result["ERROR"] = "At least one value is not numeric."
return result
}
// Hours validation, the first array item.
if (i == 0) {
Console(`VALIDATING HOURS: ${tPart}`)
// Comparison operators coerce Text to Number for comparison.
if (tPart > 23 || tPart < 0) {
result["ERROR"] = "Hours must be 0-23."
return result
}
}
// Minutes validation, the second array item.
else if (i == 1) {
Console(`VALIDATING MINUTES: ${tPart}`)
if (Count(tPart) != 2) {
result["ERROR"] = "Minutes must be two digits."
return result
}
else if (tPart > 59 || tPart < 0) {
result["ERROR"] = "Minutes must be 0-59."
return result
}
}
}
return result
}
//---------------------------------------------------------------------//
// INPUTS
// Expects($feature, "Shape_Length", "MPH", "Miles")
var time = $feature.Hiking_HHMM
// DERIVED
var timeArray = Split(time, ":")
Console(`TIME ARR: ${timeArray}`)
//---------------------------------------------------------------------//
// MAIN
var fldUpdates = Dictionary()
// result = {"VALUE": time, "ERROR": error string OR null}
var result = timeValidate(time)
// If an error is returned in results dict, show and make no changes.
if (!IsEmpty(result["ERROR"]))
{return {"errorMessage": `ERROR: ${result} Please use H?H?:MM format.`}}
// Else, add the field update information to the running updates log.
else
{fldUpdates["Hiking_HHMM"] = time}
Console({"result": {"attributes": fldUpdates}})
return {"result": {"attributes": fldUpdates}}
... View more
03-14-2024
09:45 PM
|
1
|
0
|
390
|
POST
|
For "data" reasons, a great solution--at the resolution I require, even minutes would work. My issue is that if a user is transcribing a record from elsewhere, say "7:37", they have to do the math to convert those hours into minutes (or hours and minutes into seconds, if seconds are my unit). Not a huge deal, but an extra step, and possibly error-prone. The other thing is quickly reading the table. I want to be able to read these records at a glance. Number of seconds (beyond a minute or two) means virtually nothing to me, and even minutes can get difficult: 407 minutes takes me a little while to process. So I'd really want that to show as HH:MM. And I don't know of a table "display" setting that would allow the math required to calculate that from seconds/minutes alone, and an Attribute Rule would... change the actual underlying data value? Plus, an Attribute Rule returning a value with the colon would be problematic with a DOUBLE field type. Storing as minutes is probably going to be the only solution here, but it's annoying that it can't be displayed in an easily readable way. My best bet might be to use an Attribute Rule to calculate the minutes into a SHORT/DOUBLE field from a TEXT field. So the user can type in 5:49 or whatever in the TEXT field, and then an Attribute Rule calculates the raw number of minutes into a different numeric field, and then valid sorting can take place on the MINUTES field. Appreciate the comment, thanks!
... View more
03-14-2024
07:15 PM
|
0
|
0
|
401
|
POST
|
I would like to store elapsed time as an attribute in a feature class, displayed as HH:MM (drive time, for example). I have tried several approaches, and none of them are great. "Text" Data Type - User inputs elapsed time as HH:MM, and an Attribute Rule checks that the input fits this format. Works fine, but won't sort in ascending/descending order obviously, and can't easily add/subtract time from the data. Visually good, but the "data" doesn't really mean anything. "Time Only" Data Type - This isn't really appropriate either, as my "time" data isn't wall time, but elapsed time. This field type doesn't allow me to store 0:30, for example. Also, stored time can't be larger than 23:59. "Short" Data Type - I have this set to display as ##:##, so if a user inputs 337, this shows as 3:37. This field type allows sorting, which is ideal. But, the underlying data is now pretty meaningless once it's separated from it's nice HH:MM display. So I'm hoping to come up with a way of entering elapsed time as "HH:MM" in a field that also stores the underlying data in a meaningful way (quantifiable, sortable, etc.). I would be happy to use Attribute Rules to assist with this.
... View more
03-14-2024
04:05 PM
|
0
|
4
|
485
|
POST
|
Your point about data types (String vs. NoneType) is an interesting one I hadn't considered, thanks for that. I can't replicate Pro (3.X) throwing an error regarding "nulling" a non-nullable field though, unless you're talking about using Field Calculator to "<Null>" a field, in which case it does yield an error. Thanks again for the detailed response.
... View more
03-04-2024
03:39 PM
|
0
|
0
|
630
|
Title | Kudos | Posted |
---|---|---|
1 | Wednesday | |
1 | 03-29-2024 01:30 PM | |
2 | 03-21-2024 03:12 PM | |
1 | 03-14-2024 09:45 PM | |
2 | 11-17-2022 07:46 AM |
Online Status |
Offline
|
Date Last Visited |
Thursday
|