Working with shape geometries (1.7+ API)¶
- Status: unverified
- Applies to: Metashape Pro 2.x and Metashape Standard 2.x. The shape geometry API was introduced in Metashape 1.7 (2021); pre-1.7 scripts using
shape.verticesneed rewriting. Tier 1 introspection on Metashape 2.2.3 confirms the full API surface. - Edition: Pro / Standard
- Diátaxis: how-to
- Confidence: medium
- Last reviewed: 2026-05-26
Confidence: medium. The 1.7+ shape geometry API (
Geometry.Polygon,Shape.geometry) is introspection-confirmed; the migration from 1.xshape.verticesis forum-attested with permalinks.
Problem¶
You have a script that does any of:
- Reads vertex coordinates from a shape (
shape.vertices[0]) - Creates a new shape programmatically
- Iterates over a polygon's edges
- Checks whether a shape's vertices are attached to markers
…and the script either errors with AttributeError: 'Shape'
object has no attribute 'vertices' or silently produces
garbage on Metashape 1.7 or later. The Shape API changed
fundamentally between 1.6 and 1.7, and the new structure is
not obviously discoverable from forum-era code samples.
Context¶
In Metashape 1.7, Shape gained a GeoJSON-aligned geometry
model. Vertex coordinates are no longer at shape.vertices;
they are at shape.geometry.coordinates — a nested list
structure. The pivot is canonical, attested verbatim:
"For shape properties you should now access shape.geometry (actually, should be available in 1.7 API as well)." — Alexey Pasumansky, 2021-11-30, Metashape 1.8.0 pre-release (permalink)
The API mirrors the GeoJSON
specification:
each Geometry has a type (one of 7 values: Point,
LineString, Polygon, MultiPoint, MultiLineString,
MultiPolygon, GeometryCollection) and a corresponding
coordinates shape. For single-part shapes, the outermost
index is always [0]; the actual vertex array sits inside it.
Solution¶
The 7 geometry types and their coordinates shape¶
Tier 1 introspection on Metashape 2.2.3 confirms Geometry.Type
has 7 values. Each has a different coordinates structure
(GeoJSON-aligned):
geometry.type |
Visual | coordinates structure |
Example access |
|---|---|---|---|
Point |
one vertex | list[Vector] of length 1 |
coords[0] |
LineString |
a polyline | list[Vector] |
coords[N] for vertex N |
Polygon |
one polygon (with optional inner holes) | list[list[Vector]] (outer ring first; inner rings after) |
coords[0][N] for outer vertex N |
MultiPoint |
scattered vertices | list[Vector] |
coords[N] |
MultiLineString |
several polylines | list[list[Vector]] |
coords[L][N] for line L vertex N |
MultiPolygon |
several polygons | list[list[list[Vector]]] |
coords[P][0][N] for polygon P outer vertex N |
GeometryCollection |
mixed types | geometries attribute (list of Geometry) |
iterate geom.geometries |
The constructor argument shape differs from the
coordinates shape — see the next section. Polygon's
constructor takes a flat list[Vector] (the exterior ring),
but its coordinates attribute returns nested [[Vector,
Vector, ...]] (the wrapped exterior ring, plus optional inner
rings). This is the convenience overload: the constructor
accepts the simpler form, the data structure preserves the
GeoJSON nesting.
Reading vertex coordinates¶
For a single-polygon shape (the common case for orthomosaic patch boundaries):
import Metashape
chunk = Metashape.app.document.chunk
shape = chunk.shapes[0] # first shape
# For a Polygon, coordinates[0] is the outer ring.
outer_ring = shape.geometry.coordinates[0]
for vertex in outer_ring:
print(f" vertex: {vertex.x}, {vertex.y}, {vertex.z}")
For a single-point shape:
# Point.coordinates is a list of length 1 wrapping the vertex.
vertex = shape.geometry.coordinates[0] # a Vector
The coordinates shape differs by geometry type — Point
gets list[Vector] of length 1; Polygon gets nested rings;
see the table above for the per-type access pattern.
The list-of-lists addressing convention, attested verbatim:
"shapes in 1.8 version may be represented by the collection of multiple elements and will appear as list of lists via API. So if there's only single shape, then to access the coordinates of the Nth vertex you should use:
shape.geometry.coordinates[0][N]" — Alexey Pasumansky, 2021-11-30, Metashape 1.8.0 pre-release (permalink)
Adding a shape programmatically¶
Demo verified: ✓ partial — Tier 1 introspection on Metashape 2.2.3 confirmed the constructor signatures via live invocation:
Polygon(exterior_ring)with a flatlist[Vector]works and producescoordinates = [[V1, V2, V3, V1]]; the nested-list form raisesValueError. Point and LineString constructors confirmed by introspection. Tier 3 reproduction with end-to-end shape creation in a real chunk is still pending.
The 1.7+ pattern: create the Shape via chunk.shapes.addShape(),
then assign a Geometry object to its geometry attribute:
import Metashape
chunk = Metashape.app.document.chunk
# Add a single-point shape.
point = chunk.shapes.addShape()
point.geometry = Metashape.Geometry.Point(Metashape.Vector([x, y, z]))
# Add a polygon shape (single outer ring; no inner holes).
# Constructor signature: Polygon(exterior_ring, [interior_rings])
# where exterior_ring is a flat list[Vector]. The constructor
# auto-wraps into the nested coordinates structure.
poly = chunk.shapes.addShape()
poly.geometry = Metashape.Geometry.Polygon([
Metashape.Vector([x1, y1, z1]),
Metashape.Vector([x2, y2, z2]),
Metashape.Vector([x3, y3, z3]),
Metashape.Vector([x1, y1, z1]), # close the ring (first==last)
])
# After construction: poly.geometry.coordinates == [[V1, V2, V3, V1]]
# — note the outer wrap, even though the constructor took a flat list.
# Add a polyline (line string).
line = chunk.shapes.addShape()
line.geometry = Metashape.Geometry.LineString([
Metashape.Vector([x1, y1, z1]),
Metashape.Vector([x2, y2, z2]),
])
Note the closing-ring convention for polygons: the first and
last vertex must be the same Vector value. Metashape does not
auto-close the ring; an open polygon will error or produce a
degenerate shape.
Coordinate system caveat¶
Shape coordinates are in the chunk's CRS (or in chunk
local frame if the chunk is ungeoreferenced) — not in
chunk-internal coordinates. This is opposite to
chunk.region (internal coords) and chunk.tie_points
(internal coords). When mixing shapes with tie-points or
regions, transform via chunk.crs and
chunk.transform.matrix as needed. See Setting
chunk.region
for the analogous transform pattern.
Caveats and gotchas¶
is_attachedshapes return marker keys, not coordinates. When a shape's vertices are pinned to markers (shape.is_attached == True),shape.geometry.coordinatescontains integer marker keys (marker.key), notVectorobjects. Code that callsvector.xon the iterated values will raiseAttributeError. Attested verbatim:
"If the shape has attached markers to its vertices (
shape.is_attached == True), then the list ofshape.geometry.coordinateswill contain the keys of the related markers (marker.key)." — Alexey Pasumansky, 2021-11-30, Metashape 1.8.0 pre-release (permalink)
The pattern for handling attached shapes:
if shape.is_attached:
for marker_key in shape.geometry.coordinates[0]:
marker = next(m for m in chunk.markers if m.key == marker_key)
vertex = marker.position
# use vertex
else:
for vertex in shape.geometry.coordinates[0]:
# vertex is already a Vector
pass
-
Pre-1.7 scripts using
shape.verticeswill not run on 2.x. The attribute does not exist; introspection raisesAttributeError. Migrate toshape.geometry.coordinates[0]for the simple polygon case. -
GUI-drawn shapes vs script-drawn shapes — both produce the same
Geometrystructure. The GUI's Shape tools producePolygonorLineStringtypes; the Marker attached-shape tool producesis_attached==Trueshapes. -
The closing-ring convention is unforgiving. A polygon must have its first and last vertex as the same
Vector. An open polygon will silently produce a degenerate result or error inbuildOrthomosaiclater. -
shape.geometry.is_3dmatters for some operations.is_3d=Trueshapes have meaningful Z values;is_3d=Falseones have Z=0 (or undefined). Operations likeshape.geometry.areamay behave differently depending on this flag.
See also¶
- Applying Patch on multiple shapes
— uses
shape.geometry.coordinatesfor the orthomosaic patching workflow. - Mapping orthomosaic pixels back to source images
— uses
shape.geometry.coordinates[0][0]for the point-shape lookup. - Metashape Python API Reference (2.3.1), Change Log:
- §3.29 "Metashape version 1.7.0" — addition: "Added
GeometryandAttachedGeometryclasses" + "AddedShape.geometryandShape.is_attachedattributes." - §3.22 "Metashape version 1.8.0" — removal: "Removed
has_z,type,vertex_idsandverticesattributes fromShapeclass." - Metashape Python Reference (2.3.1):
Shape,Geometry,AttachedGeometry. - Forum topic 13736 — the 1.8.0 pre-release thread (4 substantive Agisoft-staff posts on the shape geometry API).
- GeoJSON specification (RFC 7946)
— the structural standard the
GeometryAPI mirrors.