# What is the hierarchical structure of the ArcPy Geometry class?

I'm trying to learn about ArcPy's geometry class.

From my limited experience, it seems like there is a hierarchical nature to the components of a geometry:

``````import arcpy
connection = "Database Connections\my_conn.sde"
feature_class = connection + "\my_owner.my_fc"

with arcpy.da.SearchCursor(feature_class, ["SHAPE@","OID@"]) as cursor:
for row in cursor:
geometry = row[0]
print("Geometry/Polyline: {}".format(row[1]))
for i, part in enumerate(geometry):
print("  Part: {}".format(i))
for j, point in enumerate(part):
print("    Point: {}".format(j))

>>>
Geometry/Polyline: 100
Part: 0
Point: 0
Point: 1
Point: 2
Point: 3
Point: 4
Part: 1
Point: 0
Point: 1
``````

I want to learn more about the levels in the Geometry class' hierarchical structure.

From looking at the Geometry Class docs, I'm wondering if the classes/levels would be organized like so:

`````` - Geometry
- PointGeometry
- Point
- Multipoint
- Point
- Polygon
- Ring (aka part)
- Point
- Polyline
- Path (aka part)
- Point
``````

Question:

Is that how the hierarchical levels in the Geometry class are structured?

It would be nice if there were a hierarchical diagram in the docs, kind of like we have for the SDE.ST_GEOMETRY spatial type. But I haven't found anything like that yet.

# Class Hierarchy

That's a reasonable assumption, but the class hierarchy for the ArcPy Geometry objects is more like this:

• Geometry
• PointGeometry
• Multipoint
• Polyline
• Polygon

You can say that "`Multipoint` is a kind of `Geometry`".

So where does `Point` come in? Each of these classes store their coordinates in one or more `Point` objects, but they do not inherit from it.

So `PointGeometry` is not a kind of `Point`, and `Point` is not a kind of `PointGeometry`. But `PointGeometry` contains a `Point`. It also contains a `SpatialReference` object, defining the Coordinate Reference System that the point is in.

Meanwhile, `Multipoint`, `Polyline` and `Polygon` each contain multiple `Point` objects.

It's worth reading up on some of the formal theory of Object-Oriented Programming here, and the distinction between inheritance and encapsulation.

# Trying out points

You can learn a lot by opening a Python window, constructing some of these objects, and querying their properties.

If you want to specify real-world geometry, you also need a `SpatialReference`.

Edited to add: Vince in the comments warns that all geometry should have a SpatialReference, to avoid your coordinates being rounded down.

Here's how you construct a PointGeometry object:

``````spatialReferenceWGS1984 = arcpy.SpatialReference("WGS 1984")
pointZero = arcpy.Point(0,0)
pointGeometryZero = arcpy.PointGeometry(pointZero, spatialReferenceWGS1984)
``````

How do you then get the contents out again? All of these:

``````pointGeometryZero.firstPoint
pointGeometryZero.lastPoint
pointGeometryZero.centroid
pointGeometryZero[0]
``````

Will return:

``````<Point (0.0, 0.0, #, #)>
``````

The constructor for `Point` takes the X coordinate, then the Y coordinate. If using degree coordinates, this means longitude, then latitude. Or use keyword arguments to make it unambiguous, which I have done here.

``````spatialReferenceWGS1984 = arcpy.SpatialReference("WGS 1984")
pointTokyoTower = arcpy.Point(X=139.745556, Y=35.658611)
pointGeometryTokyoTower = arcpy.PointGeometry(pointTokyTower, spatialReferenceWGS1984)
``````

# Trying out Polyline

Let's build this shape:

I tried to do this:

``````outerRingPolyline = arcpy.Polyline([arcpy.Point(0,0), arcpy.Point(4,0), arcpy.Point(4,3), arcpy.Point(0,3)], spatialReferenceWGS1984)
``````

Which returned this error:

``````Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\geometries.py", line 218, in __init__
super().__init__(inputs, spatial_reference, has_z, has_m)
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\arcobjects\mixins.py", line 224, in __init__
*gp_fixargs(args, True))
File "C:\Program Files\ArcGIS\Pro\Resources\ArcPy\arcpy\geoprocessing\_base.py", line 512, in <lambda>
return lambda *args: val(*gp_fixargs(args, True))
RuntimeError: Object: CreateObject cannot create geometry from inputs
``````

This happened because ArcPy requires that multiple points must by inside the ArcPy Array Object, not just in a Python array.

``````outerRingPoints = arcpy.Array([arcpy.Point(0,0), arcpy.Point(4,0), arcpy.Point(4,3), arcpy.Point(0,3)])
outerRingPolyline = arcpy.Polyline(outerRingPoints, spatialReferenceWGS1984)

innerRingPoints = arcpy.Array([arcpy.Point(1,1), arcpy.Point(3,1), arcpy.Point(3,2), arcpy.Point(1,2)])
innerRingPolyline = arcpy.Polyline(innerRingPoints, spatialReferenceWGS1984)

bothRingsPoints = arcpy.Array([outerRingPoints, innerRingPoints])
bothRingsPolyline = arcpy.Polyline(bothRingsPoints, spatialReferenceWGS1984)
``````

We can then query this object in various ways:

``````bothRingsPolyline.partCount
2
bothRingsPolyline.pointCount
8
bothRingsPolyline.length
16.0
# They're lines, so they have no area.
bothRingsPolyline.area
0.0
bothRingsPolyline.boundary()
<Multipoint object at 0x18867be2cc8[0x188681684b0]>
# We can look request lines or points by array reference.
bothRingsPolyline[0]
<Array [<Point (0.0, 0.0, #, #)>, <Point (4.0, 0.0, #, #)>, <Point (4.0, 3.0, #, #)>, <Point (0.0, 3.0, #, #)>]>
bothRingsPolyline[0][0]
<Point (0.0, 0.0, #, #)>
bothRingsPolyline[1][3]
<Point (1.0, 2.0, #, #)>
``````

# Trying out Polygon

Try constructing this shape using `arcpy.Polygon`. It uses the same points as above.

Comparing with the `Polyline` results above, what do you expect the `area`, `partCount` and `pointCount` to be? Try it out and see what happens. Some of the numbers may surprise you, but you should be able to work out why.

Apologies for the long-winded answer, but there are indeed some quirky choices in the implementation of ArcPy that are worth understanding.

• Thank you, Vince. I'm among the people who wasn't aware of that truncation. So is there a SpatialReference to use when constructing abstract geometry that has nothing to do with the Earth's surface, as I've done here? Feb 15, 2022 at 21:05

To answer the question upfront, no, the ArcPy Geometry class hierarchy is not structured as shown.

In terms of previous answers, I think Dondragmer's is correct, although there are a few items I would like to add.

For the most part ArcPy is a Python wrapper around ArcGIS compiled code, whether that is ArcGIS Desktop (ArcMap, ArcGIS Pro) or ArcGIS Enterprise. You can't have ArcPy without having certain ArcGIS products installed and licensed. If one wants to learn the underlying geometry object model and class hierarchy being used by those products, look to their respective developer documentation. For ArcGIS Desktop:

Although ArcPy is effectively a Python wrapper, it doesn't mean it implements its own code structure identically to the underlying ArcGIS compiled code. For example, ArcPy doesn't have Envelope or GeometryBag although ArcGIS Desktop/ArcMap and ArcGIS Pro do in their compiled code.

For ArcPy, the standard Geometry -- ArcGIS Pro | Documentation is really the developer documentation too. That said, inspecting the ArcPy classes can give you a Geometry class hierarchy of sorts (although I am not sure what value comes from it).

``````# custom function for printing base class and subclass hierarchies
def print_classtree(cls, bases=True, level=0):
print(f"{'-'*2*level} {cls}")
if bases:
classes = cls.__bases__
else:
classes = cls.__subclasses__()
for cls in classes:
print_classtree(cls, bases, level + 1)
``````

Exploring ArcPy Polygon:

``````>>> # look up the Polygon class tree
>>> print_classtree(arcpy.Polygon, True)
<class 'arcpy.arcobjects.geometries.Polygon'>
-- <class 'arcpy.arcobjects.arcobjects.Geometry'>
---- <class 'arcpy.arcobjects.mixins.GeometrySpecializationMixin'>
------ <class 'object'>
---- <class 'arcpy.arcobjects._base._BaseArcObject'>
------ <class 'object'>
>>>
>>> # look down the Polygon class tree
>>> print_classtree(arcpy.Polygon, False)
<class 'arcpy.arcobjects.geometries.Polygon'>
>>>
``````

One can see `Polygon` inherits from `Geometry` which subsequently inherits from `GeometrySpecializationMixin` and `_BaseArcObject`. One can also see `Polygon` is a dead-end in terms of class hierarchy, nothing inherits from it.

If nothing inherits from Polygon, then it raises the question of how `Point` fits into the hierarchy.

Exploring ArcPy Point:

``````>>> # look up the Point class tree
>>> print_classtree(arcpy.Point, True)
<class 'arcpy.arcobjects.arcobjects.Point'>
-- <class 'arcpy.arcobjects.mixins.PointMixin'>
---- <class 'object'>
-- <class 'arcpy.arcobjects._base._BaseArcObject'>
---- <class 'object'>
>>>
>>> # look down the Point class tree
>>> print_classtree(arcpy.Point, False)
<class 'arcpy.arcobjects.arcobjects.Point'>
>>>
``````

One can see that `Point` breaks from the `Geometry` hierarchy at `_BaseArcObject`, which is near the root of ArcPy structure overall.

I tend to think of `Point` as an information exchange format and not a fundamental part of ArcPy geometries. The various ArcPy geometry constructors understand what a `Point` is and can construct a geometry from an `Array` of them, but the geometries are not fundamentally made up of them under the hood. How exactly a geometry is structured is defined somewhere besides ArcPy and is implemented in ArcGIS compiled code.

Exploring ArcPy Geometry:

Looking at the ArcPy classes, it is interesting to note there are more geometry classes than Esri covers in the documentation.

``````>>> # look down the Geometry class tree
>>> print_classtree(arcpy.Geometry, False)
<class 'arcpy.arcobjects.arcobjects.Geometry'>
-- <class 'arcpy.arcobjects.geometries.Annotation'>
-- <class 'arcpy.arcobjects.geometries.Dimension'>
-- <class 'arcpy.arcobjects.geometries.Multipatch'>
-- <class 'arcpy.arcobjects.geometries.Multipoint'>
-- <class 'arcpy.arcobjects.geometries.PointGeometry'>
-- <class 'arcpy.arcobjects.geometries.Polygon'>
-- <class 'arcpy.arcobjects.geometries.Polyline'>
>>>
``````

So how do we construct a `Multipatch`?

The only documentation I'm aware of showing you the relationships of all the different geometry classes is the ArcObjects object module diagram. I don't know how relevant this is to ArcGIS Pro and it's development environment.

@Vince mentioned this diagram:

Class Diagram of the ESRI.ArcGIS.Mobile.Geometries namespace

It’s not for ArcPy, but the structure is pretty close to ArcPy’s structure. So it’s still fairly helpful.