geos.js
Version:
an easy-to-use JavaScript wrapper over WebAssembly build of GEOS
134 lines (126 loc) • 5.4 kB
text/typescript
import { type Geometry, GeometryRef } from './Geometry.mjs';
import { P_CLEANUP, P_FINALIZATION, P_POINTER, POINTER } from '../core/symbols.mjs';
import { geos } from '../core/geos.mjs';
declare const __PREPARED__: unique symbol;
/**
* Branded type representing one of [geometries]{@link Geometry} that was
* [prepared]{@link prepare}.
*
* Geometry preparation is especially useful when a certain geometry will be
* compared many times with other geometries when computing spatial predicates
* or calculating distances.
*
* @template G - The type of prepared geometry, for example, {@link Geometry}
* or more specific {@link Polygon}
*/
export type Prepared<G extends Geometry> = G & { [ __PREPARED__ ]: true }; // branded type
/**
* Prepares geometry to optimize the performance of repeated calls to specific
* geometric operations.
*
* The "prepared geometry" is conceptually similar to a database "prepared
* statement": by doing up-front work to create an optimized object, you reap
* a performance benefit when executing repeated function calls on that object.
*
* List of functions that benefit from geometry preparation:
* - {@link distance}
* - {@link nearestPoints}
* - {@link distanceWithin}
* - {@link intersects}
* - {@link disjoint}
* - {@link contains}
* - {@link containsProperly}
* - {@link within}
* - {@link covers}
* - {@link coveredBy}
* - {@link crosses}
* - {@link overlaps}
* - {@link touches}
* - {@link relate}
* - {@link relatePattern}
*
* Modifies the geometry in-place.
*
* @template G - The type of prepared geometry, for example, {@link Geometry}
* or more specific {@link Polygon}
* @param geometry - Geometry to prepare
* @returns Exactly the same geometry object, but with prepared internal
* spatial indexes
* @throws {GEOSError} on unsupported geometry types (curved)
*
* @see {@link unprepare} frees prepared indexes
* @see {@link isPrepared} checks whether a geometry is prepared
* @see {@link https://libgeos.org/usage/c_api/#prepared-geometry}
*
* @example lifecycle of the prepared geometry
* const regularPolygon = buffer(point([ 0, 0 ]), 10, { quadrantSegments: 1000 });
* const preparedPolygon = prepare(regularPolygon);
* const regularPolygonAgain = unprepare(preparedPolygon);
* // `regularPolygon`, `preparedPolygon` and `regularPolygonAgain` are exactly the same object
* // so if you do not care about TypeScript, the above can be simplified to:
* const p = buffer(point([ 0, 0 ]), 10, { quadrantSegments: 1000 });
* prepare(p);
* unprepare(p);
*
* @example to improve performance of repeated calls against a single geometry
* const a = buffer(point([ 0, 0 ]), 10, { quadrantSegments: 1000 });
* // `a` is a polygon with many vertices (4000 in this example)
* prepare(a);
* // the preparation of geometry `a` will improve the performance of repeated
* // supported functions (see list above) calls, but only those where `a` is
* // the first geometry
* const d1 = distance(a, point([ 10, 0 ]));
* const d2 = distance(a, point([ 10, 1 ]));
* const d3 = distance(point([ 10, 2 ]), a); // no benefit from prepared geometry
* const i1 = intersects(a, lineString([ [ 0, 22 ], [ 11, 0 ] ]));
* const i2 = intersects(a, lineString([ [ 0, 24 ], [ 11, 0 ] ]));
* const i3 = intersects(lineString([ [ 0, 26 ], [ 11, 0 ] ]), a); // no benefit
*/
export function prepare<G extends Geometry>(geometry: G): Prepared<G> {
if (!geometry[ P_POINTER ]) {
const pPtr = geos.GEOSPrepare(geometry[ POINTER ]);
GeometryRef[ P_FINALIZATION ].register(geometry, pPtr, geometry);
geometry[ P_POINTER ] = pPtr;
}
return geometry as Prepared<G>;
}
/**
* Frees the prepared internal spatial indexes.
*
* Call this function when you no longer need a performance boost, but need
* the geometry itself and want to reclaim some memory.
*
* The prepared internal spatial indexes will be automatically freed alongside
* the geometry itself, either when released via [`free`]{@link GeometryRef#free}
* or when geometry goes out of scope.
*
* Modifies the geometry in-place.
*
* @template G - The type of prepared geometry, for example, {@link Geometry}
* or more specific {@link Polygon}
* @param geometry - Geometry to free its prepared indexes
* @returns Exactly the same geometry object, but without prepared internal
* spatial indexes
*
* @see {@link prepare} prepares geometry internal spatial indexes
* @see {@link isPrepared} checks whether a geometry is prepared
* @see {@link https://libgeos.org/usage/c_api/#prepared-geometry}
*
* @example lifecycle of the prepared geometry
* const regularPolygon = buffer(point([ 0, 0 ]), 10, { quadrantSegments: 1000 });
* const preparedPolygon = prepare(regularPolygon);
* const regularPolygonAgain = unprepare(preparedPolygon);
* // `regularPolygon`, `preparedPolygon` and `regularPolygonAgain` are exactly the same object
* // so if you do not care about TypeScript, the above can be simplified to:
* const p = buffer(point([ 0, 0 ]), 10, { quadrantSegments: 1000 });
* prepare(p);
* unprepare(p);
*/
export function unprepare<G extends Geometry>(geometry: Prepared<G>): G {
if (geometry[ P_POINTER ]) {
GeometryRef[ P_FINALIZATION ].unregister(geometry);
GeometryRef[ P_CLEANUP ](geometry[ P_POINTER ]);
delete geometry[ P_POINTER ];
}
return geometry as G;
}