raster landuse calculation

3078
8
Jump to solution
07-31-2014 11:20 AM
InceptionWoznicki
New Contributor

Hi Guys,

 

I want to calculate the counts of each landuse type based on the unique landuse code from  each raster datasets.

 

Raster dataset consists of six major landuse and their unique codes are below:

 

1) Agriculture : Landuse code(2100, 2200, 2300, 2400)

2) Barren Land: Landuse code( 7100,7200,7300,7400, 7500, 7600)

3)Forest:          Landuse code(4110, 4120, 4210,4220, 4230, 4311, 4312,4321,4322, 4410)

4)Urban:           Landuse code(1110,1120,1130,1140,1150,1200,1211,1214,1300,1400,1410,1411,1420,1440,1462,1463,1499,1500,1600,1700,1710,1741)

5)Water:          Landuse code(1419,5100,5190)

6)Wetlands      Landuse code (1461,1711,1750,1850)

 

Also, I want to divide the Urban area further into 4 categories.

 

I have the code only for agricultural area to simply test whether the code is working or not for one category of landuse. When I ran the code neither did I get any error message nor the result for agricultural land. I know it's a pretty big description, sorry for that.  Thank you very much for your time and help!

I am providing the code below:

 

import arcpy, os from arcpy import env  #To overwrite output arcpy.env.overwriteOutput = True  #Set environment settings env.workspace = "C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster"  outws="C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster/Table"   inraster = arcpy.ListRasters("*", "GRID") for i in inraster:     flds = ("VALUE", "COUNT")       dct = {row[0]:row[1] for row in arcpy.da.SearchCursor(i, flds)}          Agriculture=[]     Barrenland=[]     Forest=[]     Urban=[]     Water=[]     Wetland=[]      for j in dct:         if (dct.keys()==2100) & (dct.keys()==2200) & (dct.keys()==2300) & (dct.keys()==2400):             ag=Agriculture.append(dct.values())             print ag         else:             break
Tags (1)
0 Kudos
1 Solution

Accepted Solutions
XanderBakker
Esri Esteemed Contributor

Hi Ian Murray‌ and Inception Woznicki‌,

I would probably still use dictionaries. They provide a flexible way of processing this kind of problems. Take a look at the code below (please note that I did not test the code, so I am sure there will still be some errors in it).

import arcpy, os

#Set environment settings

arcpy.env.overwriteOutput = True

arcpy.env.workspace = "C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster"

# you're not use this workspace in your code, but I suppose you eventually will.

# outws="C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster/Table"

# configuration of lists of values

lst_agr = [2100, 2200, 2300, 2400]              # agriculture

lst_bar = [7100, 7200, 7300, 7400, 7500, 7600]  # Barren Land

lst_for = [4110, 4120, 4210, 4220, 4230, 4311, 4312, 4321, 4322, 4410] # Forest

lst_urb = [1110, 1120, 1130, 1140, 1150, 1200, 1211, 1214, 1300, 1400,

           1410, 1411, 1420, 1440, 1462, 1463, 1499, 1500, 1600, 1700,

           1710, 1741]                          # Urban

lst_wat = [1419, 5100, 5190]                    # Water

lst_wet = [1461, 1711, 1750, 1850]              # Wetlands

# Create a dictionary with the classname vs the lists

dct_lsts = {"Agriculture": lst_agr, "Barren Land": lst_bar,

            "Forest": lst_for, "Urban": lst_urb,

            "Water": lst_wat, "Wetlands": lst_wet}

# optionally, divide the Urban area further into 4 categories.

# instead of creating 1 list, create 4 separate lists, like this:

##lst_urb1 = [1110, 1120, 1130, 1140, 1150, 1200]

##lst_urb2 = [1211, 1214, 1300, 1400, 1410, 1411]

##lst_urb3 = [1420, 1440, 1462, 1463, 1499, 1500]

##lst_urb4 = [1600, 1700, 1710, 1741]

##dct_lsts = {"Agriculture": lst_agr, "Barren Land": lst_bar,

##            "Forest": lst_for, "Urban 1": lst_urb1,

##            "Urban 2": lst_urb2, "Urban 3": lst_urb3,

##            "Urban 4": lst_urb4, "Water": lst_wat,

##            "Wetlands": lst_wet }

# loop through rasters

flds = ("VALUE", "COUNT")

lst_ras = arcpy.ListRasters("*", "GRID")

for ras in lst_ras:

    # create dct with value vs count for this raster:

    dct = {row[0]:row[1] for row in arcpy.da.SearchCursor(ras, flds)}

    # now create an initial dictionay that will hold the resulting counts per LU class

    dct_res = {lu: 0 for lu in dct_lsts.keys()}

    # now fill the resulting dictionary

    for val, cnt in dct.items():

        # determine the lu class and add count

        for lu, lst in dct_lsts.items():

            if val in lst:

                dct_res[lu] += cnt

                break

    # now print the result:

    print "\nLanduse statistics for raster: '{0}'".format(ras)

    for lu, cnt in dct_res:

        print " - {0}: {1}".format(lu, cnt)

This is what happens (if the code works):

  • On lines 11 - 18 lists of the values corresponding to a landuse class are created
  • On Lines 21 - 23 a dictionary is created that uses a landuse description as key and the list of values is the value assigned to the key
  • On Line 28 - 37 (commented) an example is shown how you could incorporate subdividing urban into 4 classes (the values assigned to each class is arbitrary)
  • I placed the line 41 outside the loop, since assigning it one time is enough
  • On line 42 - 43, a list of GRID rasters is created and the loop is started
  • Line 45 creates the dictionary with pixelvalues vs count for the current raster
  • Line 48 creates an initial dictionary with the landuse name as key and the count set to 0
  • Line 51 starts a loop through the pixel values vs count of the current raster
  • Line 53, with that loop another loop is started to go through the dictionary with the lists.
  • On line 54 a check is done to see of the current pixelvalue is inside the current list of pixel values for the current land use. If the is the case, the count is added to the count in the dictionary that holds the landuse class description vs the count This is followed by a break to step out of the inner loop (the one that goes through each landuse class and list of related pixel values)
  • Lines 59 - 61 should create a list per raster with each landuse class and the calculated count.

Writing this down, I think you could also create a reference table with the pixelvalues on each row and the corresponding landuse classname in another column and join this table to the attribute table of the raster and perform a summerize on the landuse class column, summing the count...

Kind regards,

Xander

View solution in original post

8 Replies
IanMurray
Frequent Contributor

A raster cell can only have a single value, currently you are using and statements, not or statements and it would be impossible for your value to be 2100, 2200, 2300, and 2400 simultaneously.  Try replacing your &'s with or's

0 Kudos
InceptionWoznicki
New Contributor

Thank you very much Ian! I ran the code replacing &'s by or's . still didn't get any error or the result.

0 Kudos
IanMurray
Frequent Contributor

Looking at it again I see another problem.

ag = Agriculture.append(dct.values()))

append it used to add a value or string to a list, so making a new variable equal to an append will do nothing to the variable, though it will actually append your value to the list append is being called on(in this case Agriculture)Append.jpg.

try

Agriculture.append(dct.values())

print Agriculture

InceptionWoznicki
New Contributor

Thanks again Ian!. Changed the code, but don't know still I didn't get anything.

0 Kudos
IanMurray
Frequent Contributor

Doesn't help I'm not very good with dictionaries.  If I was doing this, I'd probably convert my raster to an ASCII file, so I could run my script without ArcGIS.  Then I'd would just iterate over each line and value in the ascii and check the value against your values, and append to a list.

Xander Bakker‌, you mind taking a look at this?

0 Kudos
InceptionWoznicki
New Contributor

Thank You very much Ian! Yeah, that's a good idea.

0 Kudos
XanderBakker
Esri Esteemed Contributor

Hi Ian Murray‌ and Inception Woznicki‌,

I would probably still use dictionaries. They provide a flexible way of processing this kind of problems. Take a look at the code below (please note that I did not test the code, so I am sure there will still be some errors in it).

import arcpy, os

#Set environment settings

arcpy.env.overwriteOutput = True

arcpy.env.workspace = "C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster"

# you're not use this workspace in your code, but I suppose you eventually will.

# outws="C:/Subhasis/Project-06-02-14/Landuse/New folder/Merged/Landuse_raster/Table"

# configuration of lists of values

lst_agr = [2100, 2200, 2300, 2400]              # agriculture

lst_bar = [7100, 7200, 7300, 7400, 7500, 7600]  # Barren Land

lst_for = [4110, 4120, 4210, 4220, 4230, 4311, 4312, 4321, 4322, 4410] # Forest

lst_urb = [1110, 1120, 1130, 1140, 1150, 1200, 1211, 1214, 1300, 1400,

           1410, 1411, 1420, 1440, 1462, 1463, 1499, 1500, 1600, 1700,

           1710, 1741]                          # Urban

lst_wat = [1419, 5100, 5190]                    # Water

lst_wet = [1461, 1711, 1750, 1850]              # Wetlands

# Create a dictionary with the classname vs the lists

dct_lsts = {"Agriculture": lst_agr, "Barren Land": lst_bar,

            "Forest": lst_for, "Urban": lst_urb,

            "Water": lst_wat, "Wetlands": lst_wet}

# optionally, divide the Urban area further into 4 categories.

# instead of creating 1 list, create 4 separate lists, like this:

##lst_urb1 = [1110, 1120, 1130, 1140, 1150, 1200]

##lst_urb2 = [1211, 1214, 1300, 1400, 1410, 1411]

##lst_urb3 = [1420, 1440, 1462, 1463, 1499, 1500]

##lst_urb4 = [1600, 1700, 1710, 1741]

##dct_lsts = {"Agriculture": lst_agr, "Barren Land": lst_bar,

##            "Forest": lst_for, "Urban 1": lst_urb1,

##            "Urban 2": lst_urb2, "Urban 3": lst_urb3,

##            "Urban 4": lst_urb4, "Water": lst_wat,

##            "Wetlands": lst_wet }

# loop through rasters

flds = ("VALUE", "COUNT")

lst_ras = arcpy.ListRasters("*", "GRID")

for ras in lst_ras:

    # create dct with value vs count for this raster:

    dct = {row[0]:row[1] for row in arcpy.da.SearchCursor(ras, flds)}

    # now create an initial dictionay that will hold the resulting counts per LU class

    dct_res = {lu: 0 for lu in dct_lsts.keys()}

    # now fill the resulting dictionary

    for val, cnt in dct.items():

        # determine the lu class and add count

        for lu, lst in dct_lsts.items():

            if val in lst:

                dct_res[lu] += cnt

                break

    # now print the result:

    print "\nLanduse statistics for raster: '{0}'".format(ras)

    for lu, cnt in dct_res:

        print " - {0}: {1}".format(lu, cnt)

This is what happens (if the code works):

  • On lines 11 - 18 lists of the values corresponding to a landuse class are created
  • On Lines 21 - 23 a dictionary is created that uses a landuse description as key and the list of values is the value assigned to the key
  • On Line 28 - 37 (commented) an example is shown how you could incorporate subdividing urban into 4 classes (the values assigned to each class is arbitrary)
  • I placed the line 41 outside the loop, since assigning it one time is enough
  • On line 42 - 43, a list of GRID rasters is created and the loop is started
  • Line 45 creates the dictionary with pixelvalues vs count for the current raster
  • Line 48 creates an initial dictionary with the landuse name as key and the count set to 0
  • Line 51 starts a loop through the pixel values vs count of the current raster
  • Line 53, with that loop another loop is started to go through the dictionary with the lists.
  • On line 54 a check is done to see of the current pixelvalue is inside the current list of pixel values for the current land use. If the is the case, the count is added to the count in the dictionary that holds the landuse class description vs the count This is followed by a break to step out of the inner loop (the one that goes through each landuse class and list of related pixel values)
  • Lines 59 - 61 should create a list per raster with each landuse class and the calculated count.

Writing this down, I think you could also create a reference table with the pixelvalues on each row and the corresponding landuse classname in another column and join this table to the attribute table of the raster and perform a summerize on the landuse class column, summing the count...

Kind regards,

Xander

InceptionWoznicki
New Contributor

Hi Xander,

Thanks a lot for all your help! I really appreciate it! Specially, the way you explain each line in the code and its corresponding role. Have a good day!

Regards,

Subhasis

0 Kudos