Count Points in Polygon and symbolize on number of occurrences

3087
14
Jump to solution
05-20-2021 04:07 AM
jaykapalczynski
Frequent Contributor

I have an app that searches for a particular species from a point file

Once found it creates graphics for the point locations and grid polygons that encompass those points.

What I want to do now is symbolize those polygons based on how many points are found within them.  I am not sure how to approach this.  I think I would have to read through each polygon and determine how many points are in there and set the symbology?

So the result would be a graphics layer of polygons of varying color based on how many points are within them  

Anyone have any ideas?

 

jaykapalczynski_0-1621508788062.png

 

0 Kudos
1 Solution

Accepted Solutions
jaykapalczynski
Frequent Contributor

First off THANK YOU ALL FOR YOUR THOUGHTS AND COMMENTS

They really helped steer me in the path and direction I took...

OK this is what I did and its working....

  1. Create a List of Unique types in the point Feature Class
  2. Call Function that reads this List by looping though that created unique list
  3. Each loop takes those unique value and calls another function that gets the correct field name and for that TYPE
  4. It then passes the (expression, type name and field name) to the last function which processes the data
  5. In the last function I am selecting the point records that match that TYPE value, I then create a new Point  Layer that has only those records.  I use that New Layer to Spatial Join, Statistics Analysis, Join Field Management, Calculate Field Management to Update the Polygon Layer with the number of occurrences for that TYPE in the Correct Field name that was set in the If Then Else

I used the If Then to change the TYPE value in the Data to a value that would work as a field name...so I can update the Polygon Layer as some of the TYPE values were really long

 

 

import arcpy

arcpy.env.workspace = r"E:/xxxxx/xxxx/Projects/xxx/xxx.sde"
Points = r"E:/xxxxx/xxxx/Projects/xxx/xxx.sde/Locations"
Grids = r"E:/xxxxx/xxxx/Projects/xxx/xxx.sde/Grids"

print "Started"

bList = []
expression = ""
expressionValue = ""
type= ""
rowCount = 0
uniquetypeList = []
countList = []   
txt_list = ""


# COUNT NUMBER OF RECORDS FOR EACH TYPE
def getFieldName(type):
    if type == "Needs Repair":
        return "NeedsRepair"
    elif type == "Broken":
        return "Broken"
    elif type == "Needs Replacement":
        return "NeedsReplacement"
    elif type == "Schedule Fix":
        return "ScheduleFix"
    else:
        return "nothing"

def processTypeUpdate(expressionValue, typeValues, fieldName):

    expressionValue2 = expressionValue
    typeName = typeValues
    fieldUpdateName = fieldName

    rowCount = 0
        
    arcpy.Delete_management('in_memory/PointsInPolys')   
    arcpy.Delete_management('in_memory/SS_PointsInPolys')
    arcpy.Delete_management('points_lyr')
    arcpy.Delete_management('points_lyr2')

    arcpy.MakeFeatureLayer_management(r'E:/xxxxx/xxxx/Projects/xxx/xxx.sde/Locations_Test', 'points_lyr')
    results = arcpy.SelectLayerByAttribute_management ('points_lyr', 'NEW_SELECTION', expressionValue2)
    arcpy.MakeFeatureLayer_management(results, 'points_lyr2')

    polygonID = "ID" ## unique polygon field name
    countField = fieldUpdateName  # The field name in the Grids FC to update 
    expression = "recalc(!FREQUENCY!)"
    codeblock = """def recalc(freq):
        if freq > -1:
            return freq
        else:
            return 0"""

    arcpy.SpatialJoin_analysis('points_lyr2', Grids, "in_memory/PointsInPolys")
        ### case field returns count per unique UID
    arcpy.Statistics_analysis ("in_memory/PointsInPolys", "in_memory/SS_PointsInPolys", [[polygonID, "Count"]], polygonID)
    arcpy.JoinField_management(Grids, polygonID, "in_memory/SS_PointsInPolys", polygonID, "FREQUENCY")
    arcpy.CalculateField_management(Grids, countField, expression, "PYTHON", codeblock)
    arcpy.DeleteField_management(Grids, "FREQUENCY")
    
    # GET THE CODES FROM THE RESULTS OF THE SELECTION
    rows = arcpy.SearchCursor('points_lyr2',"","","type_name")
    for row in rows:
        rowCount = rowCount + 1
        typeVal = str(row.type_name)  
        countList.append(typeVal)  
        txt_list = ','.join(countList)
    print "number of " + typeVal + " : " + str(rowCount)

    arcpy.Delete_management('in_memory/PointsInPolys')   
    arcpy.Delete_management('in_memory/SS_PointsInPolys')
    arcpy.Delete_management('points_lyr')
    arcpy.Delete_management('points_lyr2')  


    
# TAKE TYPE NAME AND GET THE CORRECT FIELD NAME
def processtypeNames(expressionValue, typeValues):
    type= str(typeValues)
    expressionValue2 = "type_name = '{}'".format(type)
    # GET THE CORRECT FIELD NAME
    fieldName = getFieldName(type)
    
    if fieldName == "nothing":
        print "No Value"
    else:
        # Call the function to actually update the Field Values
        uniques2 = processtypeUpdate(expressionValue2, type, fieldName)
        print "corrected field name is: " + fieldName
        

def uniquetypeList(uniquetypeValues):
    typeList = uniquetypeValues
    #print typeList
    for type in typeList:
        #print type
        expressionValue = "type_name = '{}'".format(type)
        uniques = processtypeNames(expressionValue, type)

    
# GET UNIQUE TYPS FROM FC CREATE LIST
for feature in arcpy.ListFeatureClasses():
    #print feature
    with arcpy.da.SearchCursor(Points,"type_name", sql_clause=(None,'ORDER BY type_name ASC')) as SCur:
      for row in SCur:
        if not row[0] in bList: # if not in list then add to list
          bList.append(row[0])
          type = row[0]
    del SCur
uniquetype = uniquetypeList(bList)

 

 

 

View solution in original post

0 Kudos
14 Replies
ReneRubalcava
Frequent Contributor

I recently did a video on this type of thing.

Here is a codepen to look at 

https://codepen.io/odoe/pen/vYxYxXm

In your case, since it's just counts, it's fairly straight forward, but could be an intensive task.

 

const query = {
  returnGeometry: true,
};
const cityResults = await cityLayerView.queryFeatures(query);
const frsResults = await frsLayerView.queryFeatures(query);
const features = [];
let temp = [...frsResults.features];
let temp2 = [];
// the loop is where you could block UI.
// You loop over polygons, then each polygon
// loops over each point to check if it's contained.
// You can mitigate this by removing found points from
// array of points with Array.splice()
for (let feat of cityResults.features) {
  const graphic = feat.clone();
  graphic.attributes.count = 0;
  temp2 = [...temp];
  for (let i = 0; i < temp2.length; i++) {
    const x = temp[i];
    if (
      x &&
      graphic.geometry &&
      x.geometry &&
      graphic.geometry.contains(x.geometry)
    ) {
      graphic.attributes.count++;
      temp.splice(i, 1);
    }
  }
  features.push(graphic);
}

 

 

I added a comment in there about the performance hit you can take and one way to mitigate it. If it's a lot of points, you might need to run this iteration in a worker.

jaykapalczynski
Frequent Contributor

Thanks a bunch and you aint kidding that this is a heavy lift.  Shifting gears a bit....wondering if I can do this via python or something.  Run a script against the dataset....

Polygon Dataset with multiple fields for individual species

Run a script and have it count the number of point occurrences (species specific) in each polygon for Species A and write the number in the corresponding field.  Then do this for every species.  Then I have a polygon FC that I can symbolize by a given field.  This should do what I want and not have to do it on the fly.

Do you know of any examples to accomplish this via python or geoprocessing tools?

0 Kudos
jaykapalczynski
Frequent Contributor

I found this but I need something that I can run and let it go through everything....this is far to manual...

 

https://gis.stackexchange.com/questions/264697/counting-number-of-points-differing-in-specific-attri...

 

BlakeTerhune
MVP Regular Contributor

Why not just count the number of results (points) you get when querying the specific species before you create the polygon? Then you can add it as an attribute on the polygon graphic when you create it and symbolize with that.

0 Kudos
jaykapalczynski
Frequent Contributor

I found this for python and I think it will work but I have to loop through each time with a different species name.  The example just calculates the total number of points...

https://gis.stackexchange.com/questions/289273/counting-points-within-polygon-with-arcpy

 

CAN I DO THIS?

I can create a list of all the unique Species Names

Then Step through each Species Name and set a definition query on the Points Layer

Use this DEFINITION query result and then get the counts and update a field with the count

Then move on to the next Species Name etc etc

 

0 Kudos
ReneRubalcava
Frequent Contributor

I should have asked this to start, but if you are using Online or Enterprise, you can use the Summarize Within analysis tool in the MapViewer Classic to do this. It will do counts and stats for you.

ReneRubalcava_0-1621615200701.png

 

But you're on the right track, pre-processing is definitely the way to go if you can. I am not strong with Python though, I can fake it when I need to.

BlakeTerhune
MVP Regular Contributor

If using Python, I would use Dissolve. You can specify a Statistics Field with count, to count the number of dissolved points for each species (by name or id or whatever key field you have). Note that

If theInput Featuresgeometry type is either point or multipoint andCreate multipart featuresis checked (MULTI_PARTin Python), the output will be a multipoint feature class. Otherwise, ifCreate multipart featuresis unchecked (SINGLE_PARTin Python), the output will be a point feature class.

So this would give you a new output feature class with a count field you can symbolize on.

0 Kudos
jaykapalczynski
Frequent Contributor

I am trying this....

  1. Loop though the Points Feature Class to get a list of Unique Species
  2. After each iteration in the loop call a Function and pass that species name
  3. In the Function I am just trying to get a count for that species.

This should loop through for each species and get me a count.

ANY thoughts to why I am getting this error?

 

ERROR :

Traceback (most recent call last):
File "E:\ArcGIS_Server_aws\Python_Scripts\CountBBASpecies\CountSpeciesinPolygon.py", line 61, in <module>
uniques = unique_values(expressionValue, species)
File "E:\ArcGIS_Server_aws\Python_Scripts\CountBBASpecies\CountSpeciesinPolygon.py", line 47, in unique_values
for row in cursor:
RuntimeError: Underlying DBMS error [[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Incorrect syntax near 'Vireo'.] [BBA2.DBO.JaysTest_BirdLocations_Test]
>>>

 

CODE:

# COUNT NUMBER OF RECORDS FOR EACH SPECIES
def unique_values(table, field):
    expressionValue = table
    species = field
    with arcpy.da.SearchCursor(Points, "common_name", where_clause=expressionValue) as cursor:
      for row in cursor:
        print row[0]
    
# GET UNIQUE SPECIES FROM FEATURE CLASS
for feature in arcpy.ListFeatureClasses():
    with arcpy.da.SearchCursor(Points,"common_name", sql_clause=(None,'ORDER BY common_name DESC')) as SCur:
      for row in SCur:
        if not row[0] in bList: # if not in list then add to list
          bList.append(row[0])
          species = row[0]
          expressionValue = "common_name = " + species
          # PASS species name to Function to Count
          uniques = unique_values(expressionValue, species)

 

 

0 Kudos
BlakeTerhune
MVP Regular Contributor

your expressionValue needs to put quotes around the species to be a valid SQL statement

expressionValue = "common_name = '{}'".format(species)