Wednesday, April 30, 2014

10.2.x - Getting Data Extents as Feature Classes (4 Methods)

Over the past couple of years, I have posted many posts on creating polygon extents from data.  Today, I am going to provide 4 ways to get the extent of a polygon.
  1. Use the 'Minimum Bounding Geometry' tool to create 'ENVELOPE' geometry types with or without the 'Group Option'.  This tool is available at the 'Advanced' level, so if you don't know how to/want to use python, this is your go to option to use.  The output is a feature class.
  2. For whole feature class extent use the arcpy.Describe(), and write it out to disk
    import arcpy
    from arcpy import env
    env.overwriteOutput = True
    fc = r"c:\States.shp"desc = arcpy.Describe(fc)
    extent = desc.extent
    pts = [arcpy.Point(extent.XMin, extent.YMin),
           arcpy.Point(extent.XMax, extent.YMin),
           arcpy.Point(extent.XMax, extent.YMax),
           arcpy.Point(extent.XMin, extent.YMax)]
    array = arcpy.Array(items=pts)
    poly = arcpy.Polygon(array)
    
    arcpy.CopyFeatures_management(poly, r"%scratchgdb%\way1")
    
  3. Method 1 for individual features: Use a cursor with extent XMin, YMin, XMax, and YMax, and arcpy.CreateFeatureClass()
    import arcpy
    from arcpy import env
    env.overwriteOutput = True
    fc = r"c:\States.shp"
    out_fc = r"%scratchgdb%\way2"
    if not arcpy.Exists(out_fc):
        arcpy.CreateFeatureclass_management(out_path="%scratchgdb%",
                                            out_name="way2", geometry_type="POLYGON",
                                            spatial_reference=arcpy.SpatialReference(4326))
    icur = arcpy.da.InsertCursor(out_fc, "SHAPE@")
    with arcpy.da.SearchCursor(fc, "SHAPE@") as rows:
        for row in rows:
            extent = row[0].extent
            pts = [arcpy.Point(extent.XMin, extent.YMin),
                   arcpy.Point(extent.XMax, extent.YMin),
                   arcpy.Point(extent.XMax, extent.YMax),
                   arcpy.Point(extent.XMin, extent.YMax)]
            array = arcpy.Array(items=pts)
            poly = arcpy.Polygon(array)
            icur.insertRow([poly])
            del array
            del poly
            del pts
            del extent
            del row
    
  4. Method 2 for individual features: Use a cursor objects and arcpy.CopyFeatures()
    import arcpy
    from arcpy import env
    env.overwriteOutput = True
    fc = r"c:\States.shp"
    out_fc = r"%scratchgdb%\way3"
    with arcpy.da.SearchCursor(fc, "SHAPE@") as rows:
        polys = []
        array = arcpy.Array()    
        for row in rows:
            extent = row[0].extent
            array.add(extent.lowerLeft)
            array.add(extent.lowerRight)
            array.add(extent.upperRight)
            array.add(extent.upperLeft)
            array.add(extent.lowerLeft)
            polys.append(arcpy.Polygon(array))
            array.removeAll()
            del row
        del array
        arcpy.CopyFeatures_management(polys, out_fc)
        del polys
    
Here are 4 examples on how to create extents using python or the built in system tools in ArcToolbox.
 Enjoy

Tuesday, April 29, 2014

Convert Your Custom Object to a Dictionary

Creating your own classes is a great in python.  It's easy and straight forward, and allows python users to mask repetitive code in our scripts, and create custom packages.

Let's assume that you are working with JSON, and you want to create a bunch of custom classes that return the data stored inside of them back as dictionaries.  This can be easily done by implementing the __iter__() in the class you are designing.

The function __iter__() returns an iterator object.  The object is required to support the iterator protocol.

For example, take the class below:

import json
class test(object):
    _a = None
    _b = None
    def __init__(self, a,b):
        self._a = a
        self._b = b
    #----------------------------------------------------------------------
    @property
    def a(self):
        """"""
        return self._a
    #----------------------------------------------------------------------
    @property
    def b(self):
        """"""
        return self._b
    #----------------------------------------------------------------------
    def __str__(self):
        """ returns object as string """
        o = {}
        for k,v in self.__iter__():
            o[k] = v
        return json.dumps(o)
        
    #----------------------------------------------------------------------
    def __iter__(self):
        """ iterator generator for public values/properties """
        attributes = [attr for attr in dir(self)
                      if not attr.startswith('__') and \
                      not attr.startswith('_')]
        for att in attributes:
            yield (att, getattr(self, att))

Here __iter__() and __str__() are implemented, along with two properties 'a' and 'b'.  From the user's standpoint, they are probably only interested in the public properties, so in the __iter__(), the properties without '__' and '_' are returned in a key/value pair object.  __str__() returns the data as JSON using the json library built into python.  __str__ is another built in class that is called when you do either a print or print str().

The result if you print out as a string is: {"a": 1, "b": 2}
For the dictionary: {'a': 1, 'b': 2}

The first is a string and the second is dictionary.

Enjoy