Documentation

Pro handbook

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;
}

See it on a map.