Calculate Field with Python/ Arcade

2892
16
Jump to solution
11-25-2020 04:45 AM
NoahLeimgruber
New Contributor III

HI all!

I want to execute a Python/Arcade command in ArcGIS Calculate Field which gives me a result for the catchment area (table).
The command should select all IDs in the list that have an X-value greater than the ID to be calculated (Here ID 1) and a Y-value less than the ID to be calculated (Here ID 1).
From the selected IDs, the respective areas should then be summed up and saved to the catchment area field.

In this example, the X and Y values of IDs 2 and 3 fulfill the condition. Thus, the respective areas would be added together, which would result in 15. The result would be stored in the catchment area of ID 1.

Thank you very much for your help!

 

ID

X

Y

area

catchment area

1

1000

2000

23

? (15)

2

1034

1083

10

3

1599

1743

5

4

1403

2004

34

 

1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

It seems to be working:

XanderBakker_0-1606402284990.png

 

 I did notice that there are some polygons that have -1 for the H1 and H2 values and many have an H2 values that is equal to H1 +1. When selecting only those polygons that are larger than H1 and smaller than H2, you will not get any resulting polygon and a catchment area of 0. Is that correct?

 

The code I used is:

def main():
    # load arcpy
    import arcpy

    # set reference to the featureclass
    fc = r'D:\GeoNet\CatchmentArea\Data.shp' # change this

    # create dictionary with H values and areas
    flds1 = ("OID@", "H1", "H2", "SHAPE@AREA")
    dct = {r[0]: [r[1], r[2], r[3]] for r in arcpy.da.SearchCursor(fc, flds1)}

    # start update cursor to write the catchment areas
    flds2 = ("OID@", "Einzugsgeb")
    with arcpy.da.UpdateCursor(fc, flds2) as curs:
        for row in curs:
            # read values
            oidcurr = row[0]
            # determine catchment area
            catchment = SumAreaInRangeH(oidcurr, dct)
            row[1] = catchment
            curs.updateRow(row)


def SumAreaInRangeH(oidcurr, dct):
    # function to determine catchment area
    sumarea = -1
    if oidcurr in dct:
        # set reference values
        h1 = dct[oidcurr][0]
        h2 = dct[oidcurr][1]
        sumarea = 0
        for oid, lst in dct.items():
            if oid != oidcurr and lst[0] > h1 and lst[1] < h2:
                sumarea += lst[2]
    return sumarea


if __name__ == '__main__':
    main()

View solution in original post

0 Kudos
16 Replies
XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

 

I probably would advise you to use Python for this one and use a dictionary to store the values and easy access, although I wouldn't do this using a Field Calculation (preferably, I would use a standalone script). With Arcade you would probably end up using multiple requests on the data which would result in longer processing times. 

However, is this "static" data or is this data dynamic and requires an update of this logic when data is edited? In that case I would probably go for an attribute rule using Arcade.

 

Just to be clear... when you calculate the catchment area for ID 1 you don't include the area of ID 1 in the result, right?

0 Kudos
NoahLeimgruber
New Contributor III

Hello @XanderBakker

Thank you very much for your answer and your suggestions! Unfortunately I'm a **bleep** beginner when it comes to coding so I don't understand exactly what you mean in the first section.

The data is "static".

and yes that is correct, i don't want to include the area of ID 1 in the calculation

Thank you again!

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

I could write some code to do this, but it would be easier to have some (dummy) data with the same schema to test with. Any chance that you could share some (dummy) data for this?

0 Kudos
NoahLeimgruber
New Contributor III

Hi @XanderBakker 

Thank you so much, you are absolutely amazing!

I' ve created a WeTranfer link with the data inside because I could not upload the file here because it is bigger than 5MB:

https://we.tl/t-euedapBzW8

H1 refers to X and H2 to Y

by "Einzugsgeb" is meant the catchment area

XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

 

Thanks for sharing. I will have a look and post back any results or questions that may arrise. 

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

It seems to be working:

XanderBakker_0-1606402284990.png

 

 I did notice that there are some polygons that have -1 for the H1 and H2 values and many have an H2 values that is equal to H1 +1. When selecting only those polygons that are larger than H1 and smaller than H2, you will not get any resulting polygon and a catchment area of 0. Is that correct?

 

The code I used is:

def main():
    # load arcpy
    import arcpy

    # set reference to the featureclass
    fc = r'D:\GeoNet\CatchmentArea\Data.shp' # change this

    # create dictionary with H values and areas
    flds1 = ("OID@", "H1", "H2", "SHAPE@AREA")
    dct = {r[0]: [r[1], r[2], r[3]] for r in arcpy.da.SearchCursor(fc, flds1)}

    # start update cursor to write the catchment areas
    flds2 = ("OID@", "Einzugsgeb")
    with arcpy.da.UpdateCursor(fc, flds2) as curs:
        for row in curs:
            # read values
            oidcurr = row[0]
            # determine catchment area
            catchment = SumAreaInRangeH(oidcurr, dct)
            row[1] = catchment
            curs.updateRow(row)


def SumAreaInRangeH(oidcurr, dct):
    # function to determine catchment area
    sumarea = -1
    if oidcurr in dct:
        # set reference values
        h1 = dct[oidcurr][0]
        h2 = dct[oidcurr][1]
        sumarea = 0
        for oid, lst in dct.items():
            if oid != oidcurr and lst[0] > h1 and lst[1] < h2:
                sumarea += lst[2]
    return sumarea


if __name__ == '__main__':
    main()
0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi @NoahLeimgruber ,

 

Looking a bit closer to the results I notice that in the Northern part there are some polygons with a huge catchment area. I also notice that they are summing the same polygon areas. If this is about catchments I don't think that this is hydrologically correct, but maybe the calculation has a different purpose. 

0 Kudos
NoahLeimgruber
New Contributor III

I also notice that they are summing the same polygon areas.

What do you mean by that exactly?

0 Kudos
NoahLeimgruber
New Contributor III

First of all thank you for your work and your help!

 


 

 I did notice that there are some polygons that have -1 for the H1 and H2 values and many have an H2 values that is equal to H1 +1. When selecting only those polygons that are larger than H1 and smaller than H2, you will not get any resulting polygon and a catchment area of 0. Is that correct?



Yes that's right, something is not 100% correct.
I just realized that it makes more sense if the H1 or H2 value can also be the same as the ID to be calculated.
So greater and equal than H1 and less and equal than H2. (not greater than H1 and less than H2)
This solves the problem with the 0 values, because at least the own area is included.

I don't understand why some attributes have a -1 value. but we lets ignore that...

0 Kudos