npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

manpo-georeflib

v0.1.4

Published

JavaScript Library for the georeferencing functions

Readme

js-manpo-georef-lib

JavaScript library for georeferencing — transforming coordinates between two arbitrary coordinate systems (e.g. geographic lat/lng ↔ pixel/plan coordinates) using a set of user-supplied control points.

license


Table of Contents


Overview

The library implements three families of point-to-point transforms:

| Method | Description | |--------|-------------| | Affine with TIN | Triangulates the control points (Delaunay TIN), then applies a local affine transform inside the triangle that contains each query point. Best accuracy for localised maps. | | Thin Plate Spline (TPS) | Globally smooth interpolant. Exact at control points, smooth everywhere else. | | Polynomial | Polynomial regression of order 1, 2, or 3. Fast, but less accurate at the edges. |

All three also have inverse variants that transform in the opposite direction.


Installation

Via npm (recommended for bundlers)

npm install manpo-georeflib

Via CDN (for simple <script> usage)

You can include the pre-bundled UMD build directly in your HTML page via jsDelivr:

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/bundle.js"></script>

The library will be available globally as ManpoGeorefLib (e.g. new ManpoGeorefLib.PointGeoreferencer(...)).


Quick Start

import { PointGeoreferencer, Crs } from 'manpo-georeflib'

// Control points: known pairs of (geographic, pixel) coordinates
const geoPoints = [
  [140.11, 39.70],   // [longitude, latitude]
  [140.15, 39.70],
  [140.15, 39.73],
  [140.11, 39.73],
]
const pixelPoints = [
  [100, 800],        // [x, y] in image pixels
  [900, 800],
  [900, 100],
  [100, 100],
]

// Construct the georeferencer
const georef = new PointGeoreferencer(
  geoPoints,   // source CRS (CRS 1)
  pixelPoints, // target CRS (CRS 2)
  Crs.Geographic,
  Crs.Simple
)

// Transform a single point (geographic → pixel)
const px = georef.georefAffineWithTIN([140.13, 39.71])
console.log(px) // [x, y]

// Transform a batch of points
const geoBatch = [[140.12, 39.71], [140.14, 39.72]]
const pxBatch  = georef.georefAffineWithTIN(geoBatch)

// Transform using TPS for higher accuracy
const pxTPS = georef.georefTPS([140.13, 39.71])

// Inverse: pixel → geographic
const geo = georef.georefInverseAffineWithTIN([500, 450])

Coordinate System Conventions

  • Geographic points are expressed as [longitude, latitude] (x-first).
  • Simple / pixel points are expressed as [x, y].
  • Both arrays must be in the same order and have the same length; a mismatch throws immediately.

API Reference

Crs

An Enumify enum representing the two supported coordinate systems.

| Value | Description | |-------|-------------| | Crs.Geographic | Geographic coordinates: [longitude, latitude] in decimal degrees. Distances are computed using the WGS-84 geodesic model. | | Crs.Simple | 2D Cartesian coordinates: [x, y]. Distances are Euclidean. |

import { Crs } from 'manpo-georeflib'
console.log(Crs.Geographic) // Crs { }

PointGeoreferencer

The main class. Constructs a georeferencer from two parallel arrays of control points.

Constructor

new PointGeoreferencer(ctrlPts1, ctrlPts2, crs1, crs2, params)

| Parameter | Type | Default | Description | |-----------|------|---------|-------------| | ctrlPts1 | number[][] | [] | Control points in CRS 1, e.g. [[lng1,lat1], ...] | | ctrlPts2 | number[][] | [] | Corresponding control points in CRS 2, e.g. [[x1,y1], ...] | | crs1 | Crs | Crs.Geographic | Coordinate system of ctrlPts1 | | crs2 | Crs | Crs.Simple | Coordinate system of ctrlPts2 | | params | object\|null | null | Advanced options (see below) |

Throws Error if ctrlPts1.length !== ctrlPts2.length.

params options
{
  tinOnly: true,   // skip non-TIN triangle data to save memory (disables georefAffineWithTriangleContains)
  forward: {},     // reserved for future per-method options
  inverse: {}      // reserved for future per-method options
}

Forward transform methods (CRS 1 → CRS 2)

All methods accept either a single point [x, y] or a batch [[x1,y1], [x2,y2], ...].

| Method | Signature | Description | |--------|-----------|-------------| | georefAffineWithTIN | (pt, extra?, handle_exception?) | Affine transform using the Delaunay TIN. Most accurate for interior points. | | georefAffineWithTriangleContains | (pt, extra?) | Affine using the triangle that explicitly contains the point. | | georefTPS | (pt) | Thin Plate Spline interpolation. Exact at control points. | | georefPolynomial | (pt, order?) | Polynomial regression. order ∈ {1, 2, 3}, default 1. |

extra is an optional object. After the call:

  • extra.insidetrue if the point was inside the TIN, false if extrapolated.
  • extra.flippedTriangletrue if the chosen triangle has an orientation flip between the two coordinate systems (i.e., the local affine mapping is a reflection). The result is still geometrically correct but the area may be geometrically distorted.

Inverse transform methods (CRS 2 → CRS 1)

| Method | Signature | Description | |--------|-----------|-------------| | georefInverseAffineWithTIN | (pt, extra?, handle_exception?) | Inverse affine using the TIN. | | georefInverseAffineWithTriangleContains | (pt, extra?) | Inverse affine using containing triangle. | | georefInverseTPS | (pt) | Inverse TPS. | | georefInversePolynomial | (pt, order?) | Inverse polynomial regression. |

Example — checking if a point is inside the TIN

const extra = {}
const result = georef.georefAffineWithTIN([140.13, 39.71], extra)
if (!extra.inside) {
  console.log('Extrapolated (outside TIN):', result)
} else if (extra.flippedTriangle) {
  // The point is inside the TIN but lies in a triangle whose local affine
  // mapping is a reflection (orientation flip between the two CRS).
  // The coordinates are still correct, but consider this a lower-confidence result.
  console.log('Inside TIN (flipped triangle):', result)
} else {
  console.log('Inside TIN:', result)
}

GeometryLib

A static utility class for geometric operations. All methods are static.

Distance

| Method | Description | |--------|-------------| | geoDistance(p1, p2) | Geodesic distance between two [lng, lat] points (metres). | | simpleDistance(p1, p2) | Euclidean distance between two [x, y] points. | | distance(p1, p2, crs) | Dispatches to geoDistance or simpleDistance based on crs. |

Nearest / Farthest

| Method | Description | |--------|-------------| | nearestPoint(p, pts, crs) | Returns [index, point] of the nearest point in pts. | | nearestTwoPoints(p, pts, crs) | Returns the two nearest points. | | nearestThreePoints(p, pts, crs) | Returns the three nearest points. | | farthestTwoPoints(pts, crs) | Returns [i, j] indices of the most distant pair. | | sortDistance(p, pts, crs) | Returns [[idx, dist], ...] sorted ascending by distance. |

Polyline

| Method | Description | |--------|-------------| | distancePointToLinestring(p, l, crs) | Distance from point to polyline; returns [dist, nearestPt, segIdx]. | | linearRefPointOnLinestring(p, l1, l2, crs1, crs2, bf) | Projects p from polyline l1 onto corresponding polyline l2. | | propOfPointOnLinestring(p, l, segIdx, crs) | Length proportion [0, 1] of p along l. | | pointOfPropOnLinestring(prop, l, crs) | Point at proportion prop along l. |

TIN / Triangulation

| Method | Description | |--------|-------------| | generateTIN(pts) | Builds a Delaunay TIN (returns a Delaunator instance). | | trianglesInTIN(tin) | Returns all triangles as [[i,j,k], ...]. | | pointsInTIN(tin) | Returns all vertices as [[x,y], ...]. | | triangleCentroid(points) | Centroid of three points. | | centroidsOfTriangles(triangles, pts) | Centroids of all triangles. | | isTriangleContainsPoint(a, b, c, p) | Returns true if p is inside or on triangle abc. |

Affine

| Method | Description | |--------|-------------| | affineParamsOfTriangle(tri1, tri2) | Computes the 3×3 affine matrix mapping tri1tri2. | | affineTransformPoint(p, params) | Applies an affine transform to a point. | | affineTransformInversePoint(p, params) | Applies the inverse affine transform. |

Utilities

import { degsToRads, radsToDegs } from 'manpo-georeflib'

degsToRads(180)  // Math.PI
radsToDegs(Math.PI)  // 180

🌍 High-Precision Affine Projections (ProjectionLib)

Warning on Geographic Affine Distortions: When you use Crs.Geographic with an affine-based method (georefAffineWithTIN, georefPolynomial, etc.), the library computes the affine matrix directly on the [lon, lat] degrees as if they were a flat Cartesian plane. Because 1° of longitude shrinks as you move away from the equator, this introduces affine shear and rotation distortion at high latitudes or across very large map areas.

To avoid this, you can use the built-in ProjectionLib to dynamically project your points to UTM (Universal Transverse Mercator) meters before applying an affine transform:

import { PointGeoreferencer, Crs, ProjectionLib } from 'manpo-georeflib';

// 1. Project your geographic control points to flat UTM meters
const ctrlPtsGeo = [[139.7, 35.6], [139.8, 35.6], [139.7, 35.7]];
const zone = ProjectionLib.getUTMZone(ctrlPtsGeo[0][0]); // Get the zone (e.g., 54)
const ctrlPtsUTM = ctrlPtsGeo.map(p => {
  const metric = ProjectionLib.wgs84ToUTM(p[0], p[1], zone);
  return [metric.x, metric.y];
});

// 2. Setup the Georeferencer using the projected (metric) control points
const ctrlPtsPixel = [[0, 0], [100, 0], [0, 100]];
// Notice we use Crs.Simple for both, since they are both now on flat Cartesian planes!
const georef = new PointGeoreferencer(ctrlPtsUTM, ctrlPtsPixel, Crs.Simple, Crs.Simple);

// 3. To transform a new geographic point, project it first:
const queryLonLat = [139.75, 35.65];
const metricQuery = ProjectionLib.wgs84ToUTM(queryLonLat[0], queryLonLat[1], zone);
const pixelResult = georef.georefAffineWithTIN([metricQuery.x, metricQuery.y]);

ProjectionLib API

  • ProjectionLib.getUTMZone(lon): Returns the standard UTM zone (1-60).
  • ProjectionLib.wgs84ToUTM(lon, lat, [zone]): Converts WGS84 degrees to UTM meters. Returns { x, y, zone, isNorthernHemisphere }.
  • ProjectionLib.utmToWGS84(x, y, zone, isNorthernHemisphere): Converts UTM meters back to [lon, lat] degrees.

Transform Methods Comparison

Benchmarked on 980 points with 20 control points (Geographic → Simple CRS):

| Method | Notes | |--------|-------| | georefAffineWithTIN | Fastest execution for localised, well-sampled maps. | | georefTPS | Highest accuracy. Heavily optimized with shared LU matrix decomposition for fast batch processing. | | georefPolynomial | Fastest to construct upfront. Accuracy degrades with order ≥ 2 outside the control point hull. |


Building for Browsers

If you're using this library via an npm bundler (Webpack, Vite, Rollup, etc), you don't need to do anything — importing from manpo-georeflib automatically resolves the ES module index.js and lets your bundler tree-shake and optimize it.

If you need a standalone <script> tag version for the browser:

npm run build   # produces dist/bundle.js (UMD format, ~300KB)

Testing

node --test test-fixes.js test-georeflib.js