Get Extent object for features returned by SearchCursor

5979
6
Jump to solution
10-23-2014 10:31 AM
JohnDye
Regular Contributor

What is a good way to get an extent object for the features returned by a da.SearchCursor with a where_clause applied? Certainly, I could apply a selection to the FeatureLayer and then use the getSelectedExtent method on the Layer object but frankly, I find SelectLayerByAttribute_management to be an expensive transaction. Add to this that I would also need to go back and clear the selection too. I just want to zoom to the extent of the returned features of the SearchCursor. Ideas?

with arcpy.da.SearchCursor(FeatureLayer, "SHAPE@", searchQuery) as rows:

    rowCount = 0

    for row in rows:

        rowCount = rowCount + 1

    print str(rowCount)

    if rowCount == 0:

        pythonaddins.MessageBox("No features found which meet criteria.",

                                "ERROR: INVALID SEARCH CRITERIA", 0)

        return

    elif rowCount == 1:

        if row[0].type == "point":

            extent = row[0].extent

            df.extent = extent

            df.scale = 5000

            arcpy.RefreshActiveView()

            del extent

            del rows

        else:

            print "Geometry is: " + str(row[0].type)

            return

    elif rowCount > 1:

        # so...now I have a bunch of features/rows returned..hmm

        # how to go about getting the full extent for these

        # returned features without applying a selection

0 Kudos
1 Solution

Accepted Solutions
ChrisPedrezuela
Occasional Contributor III

sorry I wasn't clear. the extents parameter in my function is an actual list of all the returned rows from your main, so means the function requires you to create a list of all those extent objects.


def GetExtents(extents):


    XMin = min([ext.XMin for ext in extents])
    XMax = max([ext.XMax for ext in extents])
    YMin = min([ext.YMin for ext in extents])
    YMax = max([ext.YMax for ext in extents])


    return arcpy.Extents(XMin, YMin, XMax, YMax)





extents = []


with arcpy.da.SearchCursor(lyr, field, query) as cur:


  for row in cur:


    extents.append(row[0].extent)





if len(extents) == 0:


  do something


elif len(extents) == 1:


  do something


elif len(extents) >1:


  df.extent = GetExtents(extents)

View solution in original post

0 Kudos
6 Replies
ChrisPedrezuela
Occasional Contributor III

just create a function within your script,

def GetExtents(extents):

    XMin = min([ext.XMin for ext in extents])
    XMax = max([ext.XMax for ext in extents])
    YMin = min([ext.YMin for ext in extents])
    YMax = max([ext.YMax for ext in extents])

    return arcpy.Extents(XMin, YMin, XMax, YMax)

and within your main for the qualifying condition,

df.extent = GetExtents(extents)

0 Kudos
JohnDye
Regular Contributor

I'm not sure I follow. I get what the function is doing, but I need to pass an extent object into your function. What I'm looking to do is get an extent for all of the records returned by a search cursor, so how would I pass the search cursor's returned records into the GetExtent function when it takes an extent object as it's parameter?

0 Kudos
ChrisPedrezuela
Occasional Contributor III

sorry I wasn't clear. the extents parameter in my function is an actual list of all the returned rows from your main, so means the function requires you to create a list of all those extent objects.


def GetExtents(extents):


    XMin = min([ext.XMin for ext in extents])
    XMax = max([ext.XMax for ext in extents])
    YMin = min([ext.YMin for ext in extents])
    YMax = max([ext.YMax for ext in extents])


    return arcpy.Extents(XMin, YMin, XMax, YMax)





extents = []


with arcpy.da.SearchCursor(lyr, field, query) as cur:


  for row in cur:


    extents.append(row[0].extent)





if len(extents) == 0:


  do something


elif len(extents) == 1:


  do something


elif len(extents) >1:


  df.extent = GetExtents(extents)

0 Kudos
JohnDye
Regular Contributor

Ah, now I understand. Unfortunately I didn't see this until I had already solved my problem and came back to report my solution.

What I ended up doing was actually just building a multipoint geometry and then grabbing the extent of that geometry object.

    elif rowCount > 1:

        if row[0].type == 'point':

            array = arcpy.Array()

            with arcpy.da.SearchCursor(FeatureLayer, "SHAPE@", searchQuery) as rows:

                for row in rows:

                    array.add(row[0].trueCentroid)

            points = arcpy.Multipoint(array)

            df.extent = points.extent

            arcpy.RefreshActiveView()

        else:

            print "Geometry is: " + str(row[0].type)

I think your solution is probably a little more flexible in that it will account for any geometry type. Mine on the other hand is only going to work for points.

0 Kudos
curtvprice
MVP Esteemed Contributor

Here's my attempt:

import arcpy

def SelectedExtent(lyr):

    """Find extent of selected features"""

    extents = []

    for row in arcpy.da.SearchCursor(lyr, "SHAPE@"):

        extents.append(row[0].extent)

    if extents:

        XMin = min([e.XMin for e in extents])

        XMax = max([e.XMax for e in extents])

        YMin = min([e.YMin for e in extents])

        YMax = max([e.YMax for e in extents])

        return arcpy.Extent(XMin, YMin, XMax, YMax)

    else:

        raise Exception("No features in " + lyr)

JoshuaBixby
MVP Esteemed Contributor

In your original post, you state the SelectLayerByAttribute_management tool is "an expensive transaction."  Assuming you mean expensive in terms of time, what are you using to time the tool and what are you comparing it against?

Generally, I stick with the arcpy functions and classes as much as possible and use the geoprocessing tools only when necessary or beneficial.  In this type of situation, i.e., getting the bounding extent of selected features or a subset of features, I find the geoprocessing tools are faster for most of the datasets I interact with regularly.

I have had success using a mapping layer and the SelectLayerByAttribute_management tool:

def GetExtent(fc, sql):
    lyr = arcpy.mapping.Layer(fc)
    arcpy.SelectLayerByAttribute_management(lyr, "NEW_SELECTION", sql)
    return lyr.getSelectedExtent()

Writing simple functions and using timeit on one of my datasets with ~250,000 records, I find using a map layer and SelectLayerByAttribute at least an order of magnitude faster than a search cursor.  I can't say for your datasets, but it might be worth trying the geoprocessing tools instead of relying on a search cursor.

0 Kudos