SHAPELY_POLYGON

Constructs a polygon geometry from an outer shell of coordinate pairs and an optional inner hole boundary.

A polygon area with holes can be interpreted as outer area minus interior ring areas:

A_{polygon} = A_{shell} - \sum_i A_{hole,i}

The function returns polygon WKT as the primary value, with area, perimeter, and centroid metadata.

Excel Usage

=SHAPELY_POLYGON(shell, holes)
  • shell (list[list], required): List of [x, y] coordinates for the outer boundary.
  • holes (list[list], optional, default: null): Optional list of lists of coordinates for holes. (Currently treating as single hole or needing complex parsing if multiple holes are passed from Excel 2D range).

Returns (dict): Excel Entity representing the polygon (WKT).

Example 1: Simple Triangle

Inputs:

shell
0 0
4 0
0 3

Excel formula:

=SHAPELY_POLYGON({0,0;4,0;0,3})

Expected output:

{"type":"String","basicValue":"POLYGON ((0 0, 4 0, 0 3, 0 0))","properties":{"Area":{"type":"Double","basicValue":6},"Perimeter":{"type":"Double","basicValue":12},"Centroid":{"type":"String","basicValue":"POINT (1.3333333333333333 1)"},"WKT":{"type":"String","basicValue":"POLYGON ((0 0, 4 0, 0 3, 0 0))"}}}

Example 2: Square with Hole

Inputs:

shell holes
0 0 2 2
10 0 8 2
10 10 8 8
0 10 2 8

Excel formula:

=SHAPELY_POLYGON({0,0;10,0;10,10;0,10}, {2,2;8,2;8,8;2,8})

Expected output:

{"type":"String","basicValue":"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 8 2, 8 8, 2 8, 2 2))","properties":{"Area":{"type":"Double","basicValue":64},"Perimeter":{"type":"Double","basicValue":64},"Centroid":{"type":"String","basicValue":"POINT (5 5)"},"WKT":{"type":"String","basicValue":"POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (2 2, 8 2, 8 8, 2 8, 2 2))"}}}

Example 3: Simple square polygon

Inputs:

shell
0 0
5 0
5 5
0 5

Excel formula:

=SHAPELY_POLYGON({0,0;5,0;5,5;0,5})

Expected output:

{"type":"String","basicValue":"POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))","properties":{"Area":{"type":"Double","basicValue":25},"Perimeter":{"type":"Double","basicValue":20},"Centroid":{"type":"String","basicValue":"POINT (2.5 2.5)"},"WKT":{"type":"String","basicValue":"POLYGON ((0 0, 5 0, 5 5, 0 5, 0 0))"}}}

Example 4: Pentagon shell polygon

Inputs:

shell
0 0
3 0
4 2
2 4
0 2

Excel formula:

=SHAPELY_POLYGON({0,0;3,0;4,2;2,4;0,2})

Expected output:

{"type":"String","basicValue":"POLYGON ((0 0, 3 0, 4 2, 2 4, 0 2, 0 0))","properties":{"Area":{"type":"Double","basicValue":11},"Perimeter":{"type":"Double","basicValue":12.8929},"Centroid":{"type":"String","basicValue":"POINT (1.8484848484848484 1.6363636363636365)"},"WKT":{"type":"String","basicValue":"POLYGON ((0 0, 3 0, 4 2, 2 4, 0 2, 0 0))"}}}

Python Code

from shapely import Polygon

def shapely_polygon(shell, holes=None):
    """
    Create a geometric polygon from a shell of points and optional holes.

    See: https://shapely.readthedocs.io/en/stable/reference/shapely.Polygon.html

    This example function is provided as-is without any representation of accuracy.

    Args:
        shell (list[list]): List of [x, y] coordinates for the outer boundary.
        holes (list[list], optional): Optional list of lists of coordinates for holes. (Currently treating as single hole or needing complex parsing if multiple holes are passed from Excel 2D range). Default is None.

    Returns:
        dict: Excel Entity representing the polygon (WKT).
    """
    try:
        # Helper to extract points from a list of lists or 2D range
        def get_points(data):
            pts = []
            if not isinstance(data, list):
                return []
            for row in data:
                if isinstance(row, list) and len(row) >= 2:
                    try:
                        pts.append((float(row[0]), float(row[1])))
                    except:
                        pass
            return pts

        clean_shell = get_points(shell)

        if len(clean_shell) < 3:
            return "Error: Polygon shell requires at least 3 points"

        # Handle holes. 
        # Note: Excel passing 3D structures (list of polygons) is hard. 
        # We will assume 'holes' input is EITHER:
        # 1. A single list of points (one hole)
        # 2. None/Empty

        clean_holes = []
        if holes is not None:
            # Ideally we'd support multiple holes, but representing list of lists of lists in Excel arguments is tricky.
            # For now, let's assume 'holes' is one hole if it looks like a list of points.
            one_hole = get_points(holes)
            if len(one_hole) >= 3:
                clean_holes.append(one_hole)

        poly = Polygon(clean_shell, holes=clean_holes)
        wkt_str = poly.wkt

        return {
            "type": "String",
            "basicValue": wkt_str,
            "properties": {
              "Area": {"type": "Double", "basicValue": float(poly.area)},
              "Perimeter": {"type": "Double", "basicValue": float(poly.length)},
              "Centroid": {"type": "String", "basicValue": poly.centroid.wkt},
              "WKT": {"type": "String", "basicValue": wkt_str}
            }
        }
    except Exception as e:
        return f"Error: {str(e)}"

Online Calculator

List of [x, y] coordinates for the outer boundary.
Optional list of lists of coordinates for holes. (Currently treating as single hole or needing complex parsing if multiple holes are passed from Excel 2D range).