Short answer

In GIS, vector features are built from three primitive geometry types defined by the OGC Simple Features standard (ISO 19125): a Point is a single location (0-dimensional), a LineString is an ordered sequence of vertices forming a path (1-dimensional), and a Polygon is an area enclosed by a closed boundary (2-dimensional). Each has a collection variant — MultiPoint, MultiLineString, MultiPolygon — that bundles several parts into one feature. Knowing exactly which type a layer holds, and how each format stores it, prevents most of the "it broke on conversion" surprises in spatial data handoffs.

The Simple Features hierarchy

The OGC/ISO Simple Features model defines a small, precise class tree. The concrete types you meet in practice:

  • Point — one coordinate pair (x, y), optionally with Z (elevation) and M (measure). A well, a sample location, a summit.
  • LineString — two or more vertices connected in order. A fault trace, a stream centreline, a survey line. A LinearRing is a special closed, non-self-intersecting LineString used to build polygons.
  • Polygon — exactly one exterior ring defining the outer boundary, plus zero or more interior rings (holes). A lake, a licence block, a geological unit outcrop.
  • MultiPoint / MultiLineString / MultiPolygon — a single feature made of several disjoint parts.
  • GeometryCollection — a heterogeneous bag of geometries; supported by some formats, awkward in many tools, best avoided in deliverables.

Every geometry also carries a coordinate dimension: XY, XYZ (with elevation), XYM (with a linear measure used in linear referencing), or XYZM. A "PointZ" and a "Point" are distinct types to many systems, which matters when you load 3D survey data into a 2D-typed column.

Single vs Multi: the distinction that breaks loads

The single-part vs multi-part split is the most operationally important detail. A Polygon is one contiguous area. A MultiPolygon is one feature composed of several separate areas — Hawaii as a single "state" feature, a fragmented forest stand, an outcrop split by erosion. The same logic applies to MultiPoint and MultiLineString.

This causes trouble because many data sources contain a mix of single and multi geometries in the same layer, while strict targets (PostGIS columns, GeoPackage layers) declare one geometry type. Loading mixed Polygon/MultiPolygon data into a column typed as POLYGON fails partway through. The standard fix is to promote everything to multi:

ogr2ogr -f PostgreSQL PG:"dbname=geo" mixed.gpkg \
  -nlt PROMOTE_TO_MULTI -nln geology.units

-nlt PROMOTE_TO_MULTI casts every single-part geometry to its multi equivalent so the whole layer is consistently MULTIPOLYGON. This is almost always the right call for delivery, because a MultiPolygon column can hold a one-part polygon, but a Polygon column cannot hold a two-part one.

Rings, holes, and winding order

A Polygon's geometry is its rings. The rules from the standard:

  • Exactly one exterior ring; zero or more interior rings (holes), each fully inside the exterior and not overlapping each other.
  • Every ring is closed — the last vertex equals the first.
  • Rings must be simple — no self-intersections.

Winding order (the direction vertices are listed) encodes which side is "inside," and conventions disagree across formats, which is a frequent silent bug:

  • RFC 7946 GeoJSON mandates the right-hand rule: exterior rings counter-clockwise (CCW), holes clockwise (CW).
  • Esri shapefiles use the opposite: exterior rings clockwise, holes counter-clockwise.
  • OGC WKB / PostGIS is tolerant on input but you should normalise; ST_ForcePolygonCCW / ST_ForcePolygonCW set it explicitly.

When a polygon "turns inside out," fills the whole world, or loses its holes after conversion, reversed winding is the usual culprit. ogr2ogr -lco RFC7946=YES enforces correct GeoJSON winding on output.

How each format stores geometry

The same logical Point or Polygon is physically encoded differently per format:

  • Shapefile holds one geometry type per file (the .shp shape type, e.g. type 1 = Point, 3 = PolyLine, 5 = Polygon) and notably does not distinguish single from multi — a shapefile "Polygon" can contain multiple parts and rings without a separate type. It also has no separate LineString-vs-MultiLineString concept. This loose typing is why round-tripping through shapefile loses the single/multi distinction.
  • GeoPackage (OGC standard, a SQLite container) stores geometry as a GeoPackage-flavoured WKB blob with the declared geometry type recorded in gpkg_geometry_columns. It cleanly supports Z/M and the multi types, one declared type per layer.
  • PostGIS stores EWKB in a geometry (or geography) column with an explicit type and SRID, e.g. geometry(MultiPolygon, 3857). You can query the actual mix with SELECT ST_GeometryType(geom), count(*) FROM units GROUP BY 1.
  • GeoJSON (RFC 7946) encodes geometry inline as nested coordinate arrays in WGS84 longitude, latitude order, with "type": "Polygon" etc. It carries no separate SRID — the CRS is fixed by the spec.

Inspect what you actually have before converting:

ogrinfo -so -al data.gpkg     # geometry type, feature count, SRS per layer

Worked example: auditing a delivery

Before sending a polygon layer to a client, confirm the type and validity rather than trusting the export:

-- in PostGIS
SELECT ST_GeometryType(geom) AS gtype,
       ST_IsValid(geom)      AS valid,
       count(*)
FROM geology.units
GROUP BY 1, 2;

If you see a mix of ST_Polygon and ST_MultiPolygon, promote to multi before delivery. If valid is false for any rows, repair them:

UPDATE geology.units SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom);

ST_MakeValid fixes self-intersections, ring problems, and sliver errors that would otherwise crash overlay analysis downstream.

Validation

  • Confirm the declared type with ogrinfo -so or ST_GeometryType, and decide whether single/multi is consistent.
  • Check validity with ST_IsValid / the QGIS "Check Validity" tool before any overlay, buffer, or area calculation.
  • Verify winding if the layer is destined for GeoJSON or a web client; force RFC 7946 winding on export.
  • Check dimensionality. If Z values matter (survey, geology), confirm the target column is PointZ/PolygonZ and not silently dropping the third coordinate.
  • Recount features after conversion; a drop means rejected geometries.

Common pitfalls and why they happen

  • Mixed single/multi load failures. A strict-typed column rejects the minority type. Happens because the source mixed them and shapefile origin hid the distinction. Fix with PROMOTE_TO_MULTI.
  • Inverted polygons after GeoJSON export. Reversed winding order, because shapefile and RFC 7946 disagree on ring direction.
  • Lost holes. Interior rings dropped or merged during a sloppy conversion, often tied to the same winding/validity issue.
  • Dropped Z values. Loading XYZ data into an XY-typed column silently flattens it; the geometry type mismatch is the cause.
  • Invalid self-intersecting polygons. Digitised by hand or produced by a bad union; they pass a visual check but crash ST_Intersection. Run ST_MakeValid first.

Bathyl perspective

We treat the geometry type, dimensionality, winding, and validity of a layer as part of its specification, recorded alongside CRS and schema, not as an afterthought discovered when a load fails. A delivery is "done" when another team can load it into their database, run an overlay, and publish it to a web client without it turning inside out or rejecting half the rows.

Related reading

Sources