|
POST
|
To format code: I have added another layer area intersect, it appears as a field but it isn't calculating the intersect area That's because you put the intersection with the WHA features into a separate loop. They get calculated and then overwritten without being stored in the output dict. I would also like to group the phases Fortunately, that is quite simple in this case. I used When() to get a default value if the phase is in neither of those groups (eg when it's null). // blablabla
// loop over the treatment area features
for(var ta in fsTa) {
// get the intersecting sugar area
var intSugar = Intersects(fsSugar, ta)
var sugarArea = 0
for(var s in intSugar) {
sugarArea += AreaGeodetic(Intersection(s, ta), "hectares")
}
// get the intersecting WHA area
var intWHA = Intersects(fsWHA, ta)
var WHAArea = 0
for(var w in intWHA) {
WHAArea += AreaGeodetic(Intersection(w, ta), "hectares")
}
// calculate the status value
var status = When(Includes([1, 2, 3, 4], ta.Phase), "Status 1", ta.Phase == 5, "Status 2", "Default")
// append to the output dict
var f = {attributes: {TA_Status: status, TA_Ha: AreaGeodetic(ta, "hectares"), TA_Sugar: sugarArea, TA_WHA: WHAArea}}
Push(combinedDict.features, f)
}
// convert to Featureset and return
return Featureset(Text(combinedDict))
... View more
12-07-2022
10:16 PM
|
1
|
4
|
3047
|
|
POST
|
AFAIK, there is no out of the box tool for that. This thread lists a few algorithms the get an incircle: algorithm - Largest circle inside a non-convex polygon - Stack Overflow I took one of the approaches: tesselate the polygon with a Voronoi diagram (Thiessen polygons in ArcGIS) find the node with the greates distance to the polygon's border, that's the center of the incircle def create_incircle(polygon):
add = arcpy.env.addOutputsToMap
arcpy.env.addOutputsToMap = False
# densify polygon to get rid of curves, which only have 2 vertices
polygon = polygon.densify("DISTANCE", 10, 1)
# extract vertices into a point fc
vertices = arcpy.management.CreateFeatureclass("memory", "vertices", "POINT", spatial_reference=polygon.spatialReference)
with arcpy.da.InsertCursor(vertices, ["SHAPE@"]) as cursor:
for part in polygon:
for v in part:
cursor.insertRow([v])
# create Thiessen polygons
thiessen = arcpy.analysis.CreateThiessenPolygons(vertices, "memory/thiessen")
# intersect these with themselves to get candidate points
test_points = arcpy.analysis.Intersect([thiessen], "memory/TestPoints", output_type="POINT")
# get the point inside the polygon with the greatest distance to the polygon's boundary
candidates = []
boundary = polygon.boundary()
with arcpy.da.SearchCursor(test_points, ["SHAPE@"]) as cursor:
for p, in cursor:
if p.disjoint(polygon):
continue
candidates.append([p, p.distanceTo(boundary)])
center = sorted(candidates, key=lambda c: c[1])[-1]
# buffer that point ith the distance to the boundary, return
arcpy.env.addOutputsToMap = add
return center[0].buffer(center[1])
# create the ouput fc
incircles = arcpy.management.CreateFeatureclass("memory", "Incircles")
arcpy.management.AddField(incircles, "FID", "LONG")
# create the incircles
with arcpy.da.InsertCursor(incircles, ["SHAPE@", "FID"]) as i_cursor:
with arcpy.da.SearchCursor("TestPolygons", ["SHAPE@", "OID@"]) as s_cursor:
for shp, oid in s_cursor:
ic = create_incircle(shp)
i_cursor.insertRow([ic, oid]) This is quite slow (1 - 2 seconds per feature), probably because the densification in line 5 adds lots of vertices. This can probably be optimized much, but it works as a quick-and-dirty approach. Sadly, it requires an Advanced license for the Thiessen polygons.
... View more
12-07-2022
08:18 AM
|
3
|
0
|
4704
|
|
POST
|
I think it is because coordinate priority changes the polyline directions Correct. Is there any method I can keep the line direction? Calculate 2 new fields: set FROM to 0 set TO to ShapeLength In the Create Routes tool, change the Measure Source to "Values from two fields", use the fields you just created.
... View more
12-07-2022
07:01 AM
|
1
|
0
|
1233
|
|
POST
|
Does 'Between Date1 and Date2' work from an SQL point of view? BETWEEN does work, but it includes both the start and end date, so the production values from days where you have a depot count are counted twice: once for the current count feature and once for the next. At least that's with only date values... Ironically this is not the first record in the table. No, but it is the first record that gets processed, because DARRA is the first depot. There shouldn't be problems if there is no previous count, if there are multiple counts on the same day, or if there is time involved. I didn't mean to prove against those cases, but apparently I did... I think Arcade doesn't like the SQL query. You will probably have to write it yourself, without using the @ notation. One of these might work: // Define the sql query (using one of the methods below)
// use string
var df = "Y-MM-DD HH:mm:ss"
var sql_query = `depot_ = '${depot}' AND datetime_ > '${Text(start_date, df)}' AND datetime_ <= '${Text(end_date, df)}'`
// convert to sql DATE
var df = "Y-MM-DD HH:mm:ss"
var sql_query = `depot_ = '${depot}' AND datetime_ > date '${Text(start_date, df)}' AND datetime_ <= date '${Text(end_date, df)}'`
// switch to full days, this might lead to small errors, like production only being applied to the next period
var df = "Y-MM-DD"
var sql_query = `depot_ = '${depot}' AND datetime_ > '${Text(start_date, df)}' AND datetime_ <= '${Text(end_date, df)}'`
var sql_query = `depot_ = '${depot}' AND datetime_ > date '${Text(start_date, df)}' AND datetime_ <= date '${Text(end_date, df)}'`
// use it
var production_between_dates = Filter(production_fs, sql_query) If none of these work, I'd need access to the actual data to help further. Either send a public link to the service or send me the tables in a pm.
... View more
12-07-2022
06:29 AM
|
0
|
1
|
1795
|
|
POST
|
// Access data layers from portal
var port = Portal( 'http://www.argis.com')
var fsTa = FeatureSetByPortalItem(port, 'itemid', 0)
var fsSugar = FeatureSetByPortalItem(port,'itemid', 0)
//Create empty dictionary
var sugarDict = {
fields:[
{name: "TA_Name", type: "esriFieldTypeString"},
{name: "TA_Ha", type: "esriFieldTypeDouble"},
{name: "TA_Sugar", type: "esriFieldTypeDouble"},
],
geometryType: "",
features: [],
}
// loop over the treatment area features
for(var ta in fsTa) {
// get the intersecting sugar areas
var intSugar = Intersects(fsSugar, ta)
// loop over those features and get the sum of the intersections
var sugarArea = 0
for(var s in intSugar) {
sugarArea += Area(Intersection(s, ta), "hectares")
}
// append to the output dict
var f = {attributes: {TA_Name: ta.Name, TA_Ha: Area(ta, "hectares"), TA_Sugar: sugarArea}}
Push(sugarDict.features, f)
}
// convert to Featureset and return
return Featureset(Text(sugarDict))
... View more
12-07-2022
02:23 AM
|
1
|
7
|
3067
|
|
POST
|
You can get the symbology of a layer. Form there, you'll have to follow the rabbit hole to the single symbology items. This script will loop through a list of layers in the active map, analyze their symbology and add a new field to each layer with the symbology's label value: layer_names = ["TestPolygons"]
aprx = arcpy.mp.ArcGISProject("current")
for name in layer_names:
# get the layer
layer = aprx.activeMap.listLayers(name)[0]
# get the symbology fields and items
renderer = layer.symbology.renderer
fields = renderer.fields
items = renderer.groups[0].items
# create a dict {values: label}
item_dict = {tuple(i.values[0]): i.label for i in items}
# add a new field
arcpy.management.AddField(layer, "Unit", "TEXT")
# insert the label values
with arcpy.da.UpdateCursor(layer, ["Unit"] + fields) as cursor:
for row in cursor:
key = tuple([str(x) for x in row[1:]]) # item.values stores values as string
try:
row[0] = item_dict[key]
cursor.updateRow(row)
except KeyError: # no symbology found
pass
... View more
12-07-2022
02:04 AM
|
4
|
4
|
7493
|
|
POST
|
Your first problem (Having to halve the area): No idea. I can't reproduce this, all areas are the same as when i measure them manually in Pro. The most obvious reason could be that you have doubled soil polygons. Also, compare the soil area sum against the parcel area. They should be the same. Your second problem (combine areas of the same type): Use a dictionary with the soil type as keys. If you encounter a type the first time, store its area. If you encounter it again, add its area to the stored one. //list soils in a parcel by type and acreage.
//get the features from the soil layer is overlaped by the parcel layer
var gensoils = Intersects(FeatureSetByName($map,"gensoils"),$feature)
// create a dictionary that stores the areas for each soil type
var soil_areas = Dictionary()
// create a variable that stores the sum of all soil areas
var soil_area_sum = 0
// loop over the soil polygons
for(var s in gensoils) {
// get the area of the intersection
var a = Area(Intersection($feature, s), "acre")
// add it to the sum
soil_area_sum += a
// store it in the dict
var type = s.muname
if(HasKey(soil_areas, type)) {
soil_areas[type] += a
} else {
soil_areas[type] = a
}
}
// get some general result info
var result = [
`The total area of the parcel is ${Round(Area($feature, "acres"))} Acre(s).`,
`The total area of soil in this parcel is ${Round(soil_area_sum)} Acre(s).`,
`There are ${Count(gensoils)} type(s) of General Soils.`
]
// loop over the soil types and append their area and percentage
for(var type in soil_areas) {
var a = soil_areas[type]
var p = a / soil_area_sum * 100
Push(result, `${type} ---- ${Round(p, 2)}% or ${Round(a, 2)} ac(s)`)
}
return Concatenate(result, TextFormatting.NewLine)
... View more
12-07-2022
01:03 AM
|
4
|
2
|
1969
|
|
POST
|
For completeness, here's the simple expression for a rectangle: var ring = Geometry($feature).rings[0]
var d1 = Distance(ring[0], ring[1])
var d2 = Distance(ring[1], ring[2])
return Max(d1, d2)
... View more
12-06-2022
11:51 PM
|
1
|
0
|
5583
|
|
POST
|
var distances = []
// get the polygon parts
var rings = Geometry($feature).rings
// loop over the parts
for(var r in rings) {
var ring = rings[r]
// loop over the vertices (skip first)
for(var v = 1; v < Count(ring); v++) {
Push(distances, Distance(ring[v - 1], ring[v]))
}
}
return Max(distances) This will get the longest side of any polygon, no matter the part count or the form. You could simplify it by only extracting the first ring and checking only the first three vertices (giving you the two sides), but it would not make any noticeable time difference.
... View more
12-06-2022
11:43 PM
|
2
|
5
|
5583
|
|
POST
|
Markdown doesn't work here. To format code: Can you post a screenshot of the error message? That way, we can try to backtrack to where your script fails. Does your inputfc really have a field called SURVEY_YEAR, and is that field really an integer field?
... View more
12-06-2022
07:27 AM
|
0
|
1
|
1881
|
|
POST
|
I'm not sure what you're trying to do after line 18. Can you explain that? To check if two strings are anagrams, all you need is in your lines 1 to 16. convert each string into a list of characters sort those lists if the lists are equal, the strings are anagrams. Instead of doing the conversion to list manually, you can just call list(your_string). To make it even easier, you can skip that step and call sorted(your_string). So, this is all you need to check if two strings are anagrams: def check_anagram(string_1, string_2):
return sorted(string_1.lower()) == sorted(string_2.lower()) check_anagram("a", "b")
False
check_anagram("abc", "bca")
True
check_anagram("schoolmaster", "theclassroom")
True
check_anagram("Schoolmaster", "TheClassroom")
True
check_anagram("Schoolmaster", "The Classroom")
False To get that last example to work, you need to ignore spaces: def check_anagram(string_1, string_2, ignore_spaces=False):
if ignore_spaces:
string_1 = string_1.replace(" ", "")
string_2 = string_2.replace(" ", "")
return sorted(string_1.lower()) == sorted(string_2.lower()) check_anagram("Schoolmaster", "The Classroom")
False
check_anagram("Schoolmaster", "The Classroom", True)
True
... View more
12-06-2022
07:18 AM
|
1
|
0
|
1738
|
|
POST
|
Starting from Dan's suggestion of convexHull(), which seemed good: with arcpy.da.UpdateCursor("TestPolygons", ["SHAPE@", "IntegerField"]) as cursor:
for shp, n in cursor:
n = len(shp[0]) - len(shp.convexHull()[0]) # only for singlepart polygons
cursor.updateRow([shp, n]) This works well for simple geometries: But it starts to break down for more complex geometries, where convex vertices are not always on the convex hull: I was ready to start fussing with angles between polylines when I realized that you can quite simply say whether a vertex is concave or convex: cretae a triangle of the vertex and its neighbours. If that triangle is inside the polygon, the vertex is convex, else it's concave. def get_concave_vertex_count(polygon):
n = 0
for part in polygon:
for i, vertex in enumerate(part):
prev_vertex = part[i-1]
try:
next_vertex = part[i+1]
except IndexError:
next_vertex = part[0]
triangle = arcpy.Polygon(arcpy.Array([prev_vertex, vertex, next_vertex]))
n += triangle.touches(polygon) # touch: only boundaries intersect
return n
with arcpy.da.UpdateCursor("PolygonLayer", ["SHAPE@", "ConcaveVertices"]) as cursor:
for shp, n in cursor:
n = get_concave_vertex_count(shp)
cursor.updateRow([shp, n])
... View more
12-06-2022
04:33 AM
|
1
|
0
|
2987
|
|
POST
|
It is absolutely possible. I changed your example table to reflect that the counts are not taken daily. I used the same values for depots A and B: For the production, I just assumed that depot A fills 10 sandbags per day, while depot B manages to fill 20. // load the count fs
var count_fs = Featureset(Text({
fields: [
{name: "DateOfCount", type: "esriFieldTypeDate"},
{name: "Depot", type: "esriFieldTypeString"},
{name: "Count", type: "esriFieldTypeInteger"},
],
features: [
{attributes: {DateOfCount: Number(Date(2022,11,1)), Depot: "A", Count: 100}},
{attributes: {DateOfCount: Number(Date(2022,11,6)), Depot: "A", Count: 50}},
{attributes: {DateOfCount: Number(Date(2022,11,22)), Depot: "A", Count: 150}},
{attributes: {DateOfCount: Number(Date(2022,11,1)), Depot: "B", Count: 100}},
{attributes: {DateOfCount: Number(Date(2022,11,6)), Depot: "B", Count: 50}},
{attributes: {DateOfCount: Number(Date(2022,11,22)), Depot: "B", Count: 150}},
],
geometryType: ""
}))
//return count_fs
// load the production fs
var production_fs = {
fields: [
{name: "DateOfProduction", type: "esriFieldTypeDate"},
{name: "Depot", type: "esriFieldTypeString"},
{name: "Count", type: "esriFieldTypeInteger"},
],
features: [],
geometryType: ""
}
for(var i = 0; i < 30; i++) {
Push(production_fs.features, {attributes: {DateOfProduction: Number(DateAdd(Date(2022,11,1), i, "days")), Depot: "A", Count: 10}})
Push(production_fs.features, {attributes: {DateOfProduction: Number(DateAdd(Date(2022,11,1), i, "days")), Depot: "B", Count: 20}})
}
production_fs = Featureset(Text(production_fs))
//return production_fs
// create the output dict
var out = {
fields: [
{name: "DateOfCount", type: "esriFieldTypeDate"},
{name: "Depot", type: "esriFieldTypeString"},
{name: "Count", type: "esriFieldTypeInteger"},
{name: "Taken", type: "esriFieldTypeInteger"},
{name: "Produced", type: "esriFieldTypeInteger"},
],
features: [],
geometryType: ""
}
// loop over depots
var depots = ["A", "B"]
for(var d in depots) {
// find all counts of the depot and order by date
var depot = depots[d]
var depot_count_fs = Filter(count_fs, "Depot = @depot")
depot_count_fs = OrderBy(depot_count_fs, "DateOfCount")
// loop over depot_count_fs
var previous_depot_count = First(depot_count_fs)
for(var depot_count in depot_count_fs) {
// find all entries in the production_fs between now and the previous count date
var start_date = previous_depot_count.DateOfCount
var end_date = depot_count.DateOfCount
var production_between_dates = Filter(production_fs, "Depot = @depot AND DateOfProduction > @start_date AND DateOfProduction <= @end_date")
// calculate the values
var current_count = depot_count.Count
var produced = Sum(production_between_dates, "Count")
var taken = previous_depot_count.Count + produced - current_count
// append new feature
Push(out.features, {attributes: {DateOfCount: Number(depot_count.DateOfCount), Depot: depot, Count: current_count, Taken: taken, Produced: produced}})
// set previous
previous_depot_count = depot_count
}
}
return Featureset(Text(out)) The "Taken" and "Produced" columns list the sandbags that were taken and produced between the current and the previous count. Lines 1-35 define the sample data. They should of course be replaced with code loading your actual data sets. Depending on your database and date format, you might have to change the sql clause in line 64.
... View more
12-06-2022
01:14 AM
|
1
|
3
|
1844
|
|
POST
|
AFAIK, arcpy has no native decorators. You would have to define them yourself. But, the arcpy.da.Editor class supports the with statement, which is somewhat similar to a decorator (it can execute code before and after your function). For the with statement to work, a class need to implement the __enter__ and __exit__ methods. These are called when the script enters and exits the with block. In your example, you're already using the UpdateCursor in a with block. The UpdateCursor's __enter__ and __exit__ methods open and close the cursor and take care of locking the edited tables. The Editor's __enter__ and __exit__ methods open and close the edit session. with arcpy.da.Editor("your/database.gdb") as edit:
with arcpy.da.UpdateCursor("Table", ["Field1", "Field2"]) as cursor:
for row in cursor:
#...
cursor.updateRow(row)
... View more
12-05-2022
11:13 PM
|
0
|
4
|
2130
|
|
POST
|
The ArcGIS Pro label engine has a huge amount of options for placing and fitting labels and for conflict resolution. Take a look at the docs: Label with the Maplex Label Engine—ArcGIS Pro | Documentation Some solutions for your problem: Use straight label placement instead of horizontal: Don't try to place the labels at fixed positions: Play with the Overrun and Reduce Size parameters Just force all labels to appear
... View more
12-02-2022
01:36 AM
|
1
|
1
|
5025
|
| 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
|