Well-Known Binary (WKB)
“Well-known binary” is a scheme for writing a simple features geometry into a platform-independent array of bytes, usually for transport between systems or between programs. By using WKB, systems can avoid exposing their particular internal implementation of geometry storage, for greater overall interoperability.
GEOS can read and write three “flavours” of WKB:
When reading WKB, GEOS will transparently detect the flavour of WKB and automatically read it. When writing WKB, the application developer must indicate what flavour of WKB to use, in particular when writing higher dimensional outputs.
Standard WKB supports two-dimensional geometry, and is a proper subset of both extended WKB and ISO WKB.
The WKB specification uses three basic types common to most typed languages: an unsigned byte; a 4-byte unsigned integer; and an 8-byte IEEE double.
// byte : 1 byte
// uint32 : 32 bit unsigned integer (4 bytes)
// double : double precision number (8 bytes)
In order to allow portability between systems with difference architectures, the representation of those types is conditioned by the wkbByteOrder
.
enum wkbByteOrder {
wkbXDR = 0, // Big Endian
wkbNDR = 1 // Little Endian
};
A “little endian” integer has the least-significant bytes first, hence “little”. For example, the number 1, encoded in little- and big- endian:
# Little endian
01 00 00 00
# Big endian
00 00 00 01
In practice this means that almost all WKB is encoded little endian, since most modern processors are little endian, but the existence of the wkbByteOrder
allows WKB to transport geometry easily between systems of different endianness.
The double precision numbers in the coordinates are also subject to the byte order rules. To encode geometries with more than 2 dimensions, see the Extended WKB format below.
Point {
double x;
double y;
};
Linear rings are components of the polygon type, and never appear outside of the polygon. Hence they dispense with the byte order and geometry type, since both are implicit in their location in the polygon structure.
LinearRing {
uint32 numPoints;
Point points[numPoints];
};
For most geometry types, empty geometries are indicated naturally by having numPoints
= 0.
Points do not have a numPoints
field, so POINT EMPTY
is represented by a Point
with each ordinate value set to an IEEE-754 quiet NaN value
(big endian 0x7ff8000000000000 or little endian 0x000000000000f87f).
GEOS only supports the seven original simple features geometry types.
enum wkbGeometryType {
wkbPoint = 1,
wkbLineString = 2,
wkbPolygon = 3,
wkbMultiPoint = 4,
wkbMultiLineString = 5,
wkbMultiPolygon = 6,
wkbGeometryCollection = 7
};
Other systems (eg PostGIS) support a wider range of types (for example, CircularString, CurvePolygon), and hence more geometry type numbers, but GEOS is currently unable to consume those geometries.
WKBPoint {
byte byteOrder;
uint32 wkbType; // 1
Point point;
};
WKBLineString {
byte byteOrder;
uint32 wkbType; // 2
uint32 numPoints;
Point points[numPoints];
};
WKBPolygon {
byte byteOrder;
uint32 wkbType; // 3
uint32 numRings;
LinearRing rings[numRings]
}
WKBMultiPoint {
byte byteOrder;
uint32 wkbType; // 4
uint32 numWkbPoints;
WKBPoint WKBPoints[numWkbPoints];
}
WKBMultiLineString {
byte byteOrder;
uint32 wkbType; // 5
uint32 num numWkbLineStrings;
WKBLineString WKBLineStrings[numWkbLineStrings];
}
wkbMultiPolygon {
byte byteOrder;
uint32 wkbType; // 6
uint32 numWkbPolygons;
WKBPolygon wkbPolygons[numWkbPolygons];
}
WKBGeometry {
union {
WKBPoint point;
WKBLineString linestring;
WKBPolygon polygon;
WKBGeometryCollection collection;
WKBMultiPoint mpoint;
WKBMultiLineString mlinestring;
WKBMultiPolygon mpolygon;
}
}
WKBGeometryCollection {
byte byteOrder;
uint32 wkbType; // 7
uint32 numWkbGeometries;
WKBGeometry wkbGeometries[numWkbGeometries];
}
The following bytes (in hex) make up the WKB for a LINESTRING(0 0, 1 1, 2 1)
:
01 - byteOrder(wkbNDR)
02000000 - wkbType(LineString)
03000000 - numPoints(3)
0000000000000000 - x(0.0)
0000000000000000 - y(0.0)
000000000000F03F - x(1.0)
000000000000F03F - y(1.0)
0000000000000040 - x(2.0)
000000000000F03F - y(1.0)
The original WKB specification made no allowances for adding extra dimensions, like Z and M, that are common in GIS applications. It also had no space for embedding a spatial reference identifier (SRID), which made it unusable as a database import/export format.
The “Extended WKB” variant is a superset of the standard WKB, which allows applications to optionally add extra dimensions, and optionally embed an SRID.
The presence of extra dimensions is indicated by adding flag bits to the existing wkbType
that appears in all WKB geometries.
For example, here is the structure and flagging for a 3D point – the dimensionaly flag indicates a Z dimension, and the point member therefore has three coordinates.
wkbZ = 0x80000000
wkbM = 0x40000000
wkbSRID = 0x20000000
enum wkbGeometryTypeZ {
wkbPoint = 1,
wkbLineString = 2,
wkbPolygon = 3,
wkbMultiPoint = 4,
wkbMultiLineString = 5,
wkbMultiPolygon = 6,
wkbGeometryCollection = 7,
// | 0x80000000
wkbPointZ = 0x80000001,
wkbLineStringZ = 0x80000002,
wkbPolygonZ = 0x80000003,
wkbMultiPointZ = 0x80000004,
wkbMultiLineStringZ = 0x80000005,
wkbMultiPolygonZ = 0x80000006,
wkbGeometryCollectionZ = 0x80000007,
}
WKBPointZ {
byte byteOrder; // wkbXDR or wkbNDR
uint32 wkbType; // wkbPointZ = (wkbPoint | wkbZ) = 0x80000001
Point {
Double x;
Double y;
Double z;
}
}
When the optional wkbSRID
is added to the wkbType
, an SRID number is inserted after the wkbType
number.
WKBPointS {
byte byteOrder; // wkbXDR or wkbNDR
uint32 wkbType; // wkbPointS = (wkbPoint | wkbSRID) = 0x20000001
uint32 SRID;
Point {
Double x;
Double y;
}
}
The dimensionality and SRID flags can also be combined. Here is a 3D point with an embedded SRID.
WKBPointS {
byte byteOrder; // wkbXDR or wkbNDR
uint32 wkbType; // wkbPointS = (wkbPoint | wbkZ | wkbSRID) = 0xA0000001
uint32 SRID;
Point {
Double x;
Double y;
Double z;
}
}
/* Read a linestring */
const char* linestring = "LINESTRING(0 0 1, 1 1 1, 2 1 2)";
GEOSWKBReader* reader = GEOSWKBReader_create();
GEOSGeom* geom = GEOSWKTReader_read(reader, linestring);
GEOSSetSRID(geom, 4326);
/* Write it out as Extended WKB */
GEOSWKBWriter* writer = GEOSWKBWriter_create();
/* Next line only needed before GEOS 3.12 */
GEOSWKBWriter_setOutputDimension(writer, 3);
GEOSWKBWriter_setFlavor(writer, GEOS_WKB_EXTENDED);
GEOSWKBWriter_setIncludeSRID(writer, 1);
/* Generate the WKB , and store the output length */
size_t wkb_size;
unsigned char* wkb = GEOSWKBWriter_write(writer, geom, &wkb_size);
/* do something ... */
/* Free the WKB */
GEOSFree(wkb);
GEOSGeom_destroy(geom);
ISO WKB was included in the ISO 13249-3 document (SQL/MM Part 3: Spatial). It allows for higher dimensional geometries, but does not have a facility for SRID embedding.
As with extended WKB, ISO WKB supports higher dimensions by adding information to the wkbType
number. Rather than using flag bits, however, ISO WKB simply adds a round number to the type number to indicate extra dimensions.
- wkbType + 1000 = wkbTypeZ
- wkbType + 2000 = wkbTypeM
- wkbType + 3000 = wkbTypeZM
/* Read a linestring */
const char* linestring = "LINESTRING(0 0 1, 1 1 1, 2 1 2)";
GEOSWKTReader* reader = GEOSWKTReader_create();
GEOSGeom* geom = GEOSWKTReader_read(reader, linestring);
/* Write it out as ISO WKB */
GEOSWKBWriter* writer = GEOSWKBWriter_create();
/* Next line only needed before GEOS 3.12 */
GEOSWKBWriter_setOutputDimension(writer, 3);
GEOSWKBWriter_setFlavor(writer, GEOS_WKB_ISO);
/* Generate the WKB , and store the output length */
size_t wkb_size;
unsigned char* wkb = GEOSWKBWriter_write(writer, geom, &wkb_size);
/* do something ... */
/* Free the WKB */
GEOSFree(wkb);
GEOSGeom_destroy(geom);
GEOSWKBWriter_destroy(writer);
GEOSWKTReader_destroy(reader);