Help us shape the future of Go! Take the 2026 Go Developer Survey

twkb

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 9, 2026 License: MIT Imports: 5 Imported by: 0

README

go-twkb

Go Reference

Encode and decode TWKB (Tiny Well-known Binary) for go-geom geometries.

TWKB stores coordinates as delta-encoded varints at a fixed decimal precision instead of 8-byte floats. For GPS tracks and other dense lines it is typically 5–8 times smaller than WKB, and much smaller than gzipped WKB.

  • All seven geometry types, empty geometries, Z and M (XY, XYZ, XYM, XYZM).
  • Size, bounding box and id list options.
  • Byte-for-byte identical to PostGIS ST_AsTWKB, tested against PostGIS output, including rounding and removal of points that repeat after rounding.
  • A decoder that bounds every count by the remaining input, so malformed or hostile input cannot make it allocate more than the input justifies. Fuzzed.
  • No dependencies besides go-geom.

Usage

import "github.com/FeltGIS/go-twkb"

// Keep x/y to the centimetre and z to the decimetre.
data, err := twkb.Marshal(g, twkb.Precision(2), twkb.PrecisionZ(1))

g, err := twkb.Unmarshal(data)

Options:

Option Meaning
Precision(n) Decimal digits kept for x and y, -8..7. Negative values round to tens, hundreds and so on.
PrecisionZ(n), PrecisionM(n) Decimal digits kept for z and m, 0..7.
WithSize() Write the byte size of each geometry, so readers can skip it.
WithBBox() Write the bounding box of each geometry.
WithIDs(ids) Write one id per member of a multi geometry or collection. Read them back with UnmarshalWithIDs.
KeepRepeatedPoints() Keep points that become identical to the previous point after rounding. By default they are dropped like PostGIS does, keeping at least 2 points per line and 4 per ring.

Choose the precision for your coordinate system: in a projected system in metres, Precision(1) keeps decimetres; in longitude/latitude, Precision(5) is about a metre and Precision(7) about a centimetre.

How much smaller?

Measured with this package on harvester track logs (UTM 33N, metres):

Geometry WKB WKB + gzip TWKB, 0.1 m
Real track log from one harvest 59 KB 46 KB 12 KB
Synthetic track, 100 000 points 1.6 MB – 202 KB
Synthetic track, 2 000 points 32 KB – 4.0 KB
Same, with elevation (z to 0.1 m) 48 KB – 6.0 KB

Elevation costs WKB 8 bytes per point and TWKB about 1.

Command line

go install github.com/FeltGIS/go-twkb/cmd/twkb@latest

echo 'LINESTRING(264776.12 6787281.98,264778.5 6787283.25)' | twkb encode -p 2
echo 420002d890a0198cc8a48705dc03fe01 | twkb decode -out geojson

encode reads WKT, GeoJSON or hex WKB (-in), decode writes the same formats (-out). TWKB is hex, one geometry per line.

Differences from PostGIS

  • Multi points with empty members cannot be encoded.
  • Unmarshal returns a *geom.GeometryCollection without an explicit layout; its Layout() is the widest layout of its members.

License

MIT. Made by FeltGIS.

Documentation

Overview

Package twkb encodes and decodes geometries in Tiny Well-known Binary (TWKB) for github.com/twpayne/go-geom.

TWKB (https://github.com/TWKB/Specification) stores coordinates as delta-encoded, zigzag varint integers at a chosen decimal precision. For GPS tracks it is typically 4-8 times smaller than WKB, which stores every coordinate as an 8-byte float.

The encoder follows the behaviour of PostGIS ST_AsTWKB so that output is byte-for-byte identical for the same input and options:

  • Coordinates are scaled by 10^precision and rounded half away from zero.
  • Within a geometry, deltas continue across rings and parts; each member of a geometry collection starts from zero with its own header.
  • Points in a line or ring that become identical to the previous point after rounding are dropped, keeping at least 2 points per line and 4 per ring. Use KeepRepeatedPoints to keep them.
  • Members of a geometry collection inherit the size and bounding box options.
Example
package main

import (
	"fmt"

	"github.com/twpayne/go-geom"
	"github.com/twpayne/go-geom/encoding/wkb"

	"github.com/FeltGIS/go-twkb"
)

func main() {
	// A short GPS track in UTM 33N, kept to the centimetre, with elevation
	// kept to the decimetre.
	track := geom.NewLineStringFlat(geom.XYZ, []float64{
		264776.12, 6787281.98, 512.3,
		264778.50, 6787283.25, 512.8,
		264781.75, 6787280.50, 513.0,
	})
	data, err := twkb.Marshal(track, twkb.Precision(2), twkb.PrecisionZ(1))
	if err != nil {
		panic(err)
	}
	wkbData, _ := wkb.Marshal(track, wkb.NDR)
	fmt.Printf("WKB %d bytes, TWKB %d bytes\n", len(wkbData), len(data))

	g, err := twkb.Unmarshal(data)
	if err != nil {
		panic(err)
	}
	c := g.(*geom.LineString).Coord(1)
	fmt.Printf("%.2f %.2f %.1f\n", c[0], c[1], c[2])
}
Output:
WKB 81 bytes, TWKB 25 bytes
264778.50 6787283.25 512.8

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidPrecision is returned for a precision outside the range TWKB
	// can store: -8..7 for x/y and 0..7 for z and m.
	ErrInvalidPrecision = errors.New("twkb: invalid precision")
	// ErrUnsupportedGeometry is returned for geometry types or layouts TWKB
	// cannot represent.
	ErrUnsupportedGeometry = errors.New("twkb: unsupported geometry")
	// ErrCoordinateOutOfRange is returned when a scaled coordinate is not a
	// finite value that fits in an int64.
	ErrCoordinateOutOfRange = errors.New("twkb: coordinate out of range")
	// ErrInvalidIDs is returned when the id list does not match the geometry.
	ErrInvalidIDs = errors.New("twkb: invalid id list")
	// ErrMalformed is returned when the input is not valid TWKB.
	ErrMalformed = errors.New("twkb: malformed input")
)

Functions

func Marshal

func Marshal(g geom.T, opts ...Option) ([]byte, error)

Marshal encodes g as TWKB.

func Unmarshal

func Unmarshal(data []byte) (geom.T, error)

Unmarshal decodes one TWKB geometry. Bytes after the geometry are ignored.

func UnmarshalWithIDs

func UnmarshalWithIDs(data []byte) (geom.T, []int64, error)

UnmarshalWithIDs decodes one TWKB geometry and the id list of its members, if it has one.

Types

type Option

type Option func(*encoder)

Option configures Marshal.

func KeepRepeatedPoints

func KeepRepeatedPoints() Option

KeepRepeatedPoints keeps points in lines and rings that are identical to the previous point after rounding. By default they are dropped, as PostGIS ST_AsTWKB does.

func Precision

func Precision(digits int) Option

Precision sets the number of decimal digits kept for x and y, -8..7. Negative values round to tens, hundreds and so on. The default is 0.

func PrecisionM

func PrecisionM(digits int) Option

PrecisionM sets the number of decimal digits kept for m, 0..7.

func PrecisionZ

func PrecisionZ(digits int) Option

PrecisionZ sets the number of decimal digits kept for z, 0..7.

func WithBBox

func WithBBox() Option

WithBBox writes the bounding box of each geometry.

func WithIDs

func WithIDs(ids []int64) Option

WithIDs writes one id per member of a multi geometry or geometry collection. The number of ids must equal the number of members.

func WithSize

func WithSize() Option

WithSize writes the size of each geometry, so readers can skip it.

Directories

Path Synopsis
cmd
twkb command
Command twkb converts geometries between TWKB and WKT, GeoJSON or WKB.
Command twkb converts geometries between TWKB and WKT, GeoJSON or WKB.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL