Utilities
Most of what @vectorstream/core, @vectorstream/adapter and @vectorstream/editor export
revolves around a partition or a live map: drawing, editing, snapping. Underneath that, a smaller
set of functions takes only plain points, numbers and strings and gives back plain values — no
partition, no editor, no map. This page collects a selection of them in one place, by package, so
they read as a library rather than disappearing between individual modules.
@vectorstream/core#
| Function | Signature | Description |
|---|---|---|
kreisPolygon |
(center: Point, rx: number, ry: number, sides: number) => Point[] |
Regular n-gon around center with semi-axes rx/ry — a circle when rx === ry, an ellipse otherwise. The same geometry draw-circle draws with. |
konvexeHuelle |
(punkte: readonly Point[]) => Point[] |
Convex hull of a point set, counterclockwise, points lying on an edge dropped. |
minimalesRechteck |
(punkte: readonly Point[]) => MinimalesRechteck |
Smallest bounding rectangle at any rotation (winkel, breite, hoehe, mitte). |
thinPolyline |
(points: readonly Point[], tolerance: number) => Point[] |
Douglas-Peucker thinning of a plain point sequence; first and last point always stay. |
polygonArea |
(points: Point[]) => number |
Signed shoelace area of an open ring, positive counterclockwise. |
pointInRing |
(pt: Point, polygon: Point[]) => boolean |
Point-in-polygon test. |
gridSize |
(exponent: number) => number |
Mesh size 2^-exponent of a coordinate grid of that exponent (see Topology). |
snapToGrid |
(v: number, exponent: number) => number |
Rounds one coordinate value to that grid. |
onGrid |
(v: number, exponent: number) => boolean |
Whether a value already lies on that grid. |
translation / rotation / scaling |
(…) => Affine |
Build an affine transform — translation, rotation or scaling, the latter two optionally about a point other than the origin. |
applyAffine |
(t: Affine, p: Point) => Point |
Applies an affine transform to a point. |
getLocale / setLocale / registerLocale |
see Language | Read or switch the current language; register a translation table. |
formatNumber |
(value: number, options?: Intl.NumberFormatOptions) => string |
Number formatting in the current locale. |
@vectorstream/adapter#
| Function | Signature | Description |
|---|---|---|
kugelAbstand |
(a: Coord, b: Coord, R: number) => number |
Great-circle distance between two longitude/latitude coordinates, in meters, on a sphere of radius R. |
kugelMetrik |
(R?: number) => Metric |
A Metric (distance, area, formatting) over a sphere of radius R, default Earth (ERD_RADIUS). |
azimut |
(a: Coord, b: Coord) => number |
Bearing from a to b, clockwise from north, in [0, 2π). |
Not listed above: a pixel-radius-to-meter conversion. kugelAbstand/kugelMetrik already turn a
distance between two coordinates into meters, but nothing here starts from a screen-pixel radius
and a zoom level the way kugelAbstand starts from two coordinates — worth adding once a real use
needs it, not ahead of one.
Not here, on purpose#
Some functions other libraries expose as free-standing utilities are, here, deliberately bound to
an Editor instance instead: copyLayer, findLayers and moveLayerTo have no equivalent on
this page, because a topology operation only makes sense inside a partition. The same work is
editor.kopieren(), the editor.ebenen list and editor.umziehen() — see Pasting
and Layers.
Example#
Center a point cloud, snap the center onto a coordinate grid, and draw a circle polygon of a given
ground radius around it — combining minimalesRechteck, snapToGrid, gridSize, kreisPolygon
and kugelAbstand, nothing else:
import { gridSize, kreisPolygon, minimalesRechteck, snapToGrid, type Point } from '@vectorstream/core';
import { ERD_RADIUS, kugelAbstand } from '@vectorstream/adapter';
/**
* None of this touches a partition, an editor or a map — plain points and numbers in, plain
* points and numbers out. Finds a point cloud's center, snaps it onto a coordinate grid, and
* draws a circle polygon of a given ground radius (in meters, measured on the sphere) around it.
*/
export function kreisUmPunktwolke(
points: readonly Point[],
gridExponent: number,
radiusMeter: number,
sides: number,
): { zentrum: Point; gitterMasche: number; ring: Point[] } {
const { mitte } = minimalesRechteck(points as Point[]);
const zentrum: Point = { x: snapToGrid(mitte.x, gridExponent), y: snapToGrid(mitte.y, gridExponent) };
// Longitude/latitude, not a Euclidean plane: a degree of longitude covers less ground the
// farther from the equator, so the x semi-axis grows by 1 / cos(latitude) to still measure
// `radiusMeter` on the ground — the same correction `DrawCircleMode` applies when it draws a
// true circle (see its `halbachsen`).
const degreesPerMeter = 180 / (Math.PI * ERD_RADIUS);
const ry = radiusMeter * degreesPerMeter;
const rx = ry / Math.cos((zentrum.y * Math.PI) / 180);
const ring = kreisPolygon(zentrum, rx, ry, sides);
return { zentrum, gitterMasche: gridSize(gridExponent), ring };
}
/** The ring's actual ground radius in meters, for checking `kreisUmPunktwolke`'s result. */
export function groesstAbweichungMeter(zentrum: Point, ring: readonly Point[], radiusMeter: number): number {
let abweichung = 0;
for (const p of ring) {
abweichung = Math.max(abweichung, Math.abs(kugelAbstand(zentrum, p, ERD_RADIUS) - radiusMeter));
}
return abweichung;
}