UNPKG

@loaders.gl/wkt

Version:

Loader and Writer for the WKT (Well Known Text) Format

196 lines (195 loc) 7.1 kB
// loaders.gl // SPDX-License-Identifier: MIT // Copyright (c) vis.gl contributors // Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz import { BinaryWriter } from "./utils/binary-writer.js"; import { WKBGeometryType } from "./parse-wkb-header.js"; export function encodeTWKB(geometry, options) { const writer = new BinaryWriter(0, true); const context = { ...getTwkbPrecision(5, 0, 0), hasZ: options?.hasZ, hasM: options?.hasM }; encodeGeometry(writer, geometry, context); // TODO - we need to slice it? return writer.arrayBuffer; } function encodeGeometry(writer, geometry, context) { switch (geometry.type) { case 'Point': return encodePoint(writer, context, geometry); case 'LineString': return encodeLineString(writer, context, geometry); case 'Polygon': return encodePolygon(writer, context, geometry); case 'MultiPoint': return encodeMultiPoint(writer, context, geometry); case 'MultiLineString': return encodeMultiLineString(writer, context, geometry); case 'MultiPolygon': return encodeMultiPolygon(writer, context, geometry); case 'GeometryCollection': return encodeGeometryCollection(writer, context, geometry); default: throw new Error('unsupported geometry type'); } } function encodePoint(writer, context, point) { const isEmpty = point.coordinates.length === 0 || point[0] === 'undefined' || point[1] === 'undefined'; writeTwkbHeader(writer, context, WKBGeometryType.Point, isEmpty); if (!isEmpty) { const previousPoint = [0, 0, 0, 0]; writeTwkbPoint(writer, context, point.coordinates, previousPoint); } } function encodeLineString(writer, context, lineString) { const points = lineString.coordinates; const isEmpty = points.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.LineString, isEmpty); if (!isEmpty) { writer.writeVarInt(points.length); const previousPoint = [0, 0, 0, 0]; for (const point of points) { writeTwkbPoint(writer, context, point, previousPoint); } } return writer.arrayBuffer; } function encodePolygon(writer, context, polygon) { const polygonRings = polygon.coordinates; const isEmpty = polygonRings.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.Polygon, isEmpty); if (!isEmpty) { writer.writeVarInt(polygonRings.length); const previousPoint = [0, 0, 0, 0]; for (const ring of polygonRings) { writer.writeVarInt(ring.length); for (const point of ring) { writeTwkbPoint(writer, context, previousPoint, point); } } } return writer.arrayBuffer; } function encodeMultiPoint(writer, context, multiPoint) { const points = multiPoint.coordinates; const isEmpty = points.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.MultiPoint, isEmpty); if (!isEmpty) { writer.writeVarInt(points.length); const previousPoint = [0, 0, 0, 0]; for (let i = 0; i < points.length; i++) { writeTwkbPoint(writer, context, previousPoint, points[i]); } } } function encodeMultiLineString(writer, context, multiLineStrings) { const lineStrings = multiLineStrings.coordinates; const isEmpty = lineStrings.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.MultiLineString, isEmpty); if (!isEmpty) { writer.writeVarInt(lineStrings.length); const previousPoint = [0, 0, 0, 0]; for (const lineString of lineStrings) { writer.writeVarInt(lineString.length); for (const point of lineString) { writeTwkbPoint(writer, context, previousPoint, point); } } } return writer.arrayBuffer; } function encodeMultiPolygon(writer, context, multiPolygon) { const { coordinates } = multiPolygon; const isEmpty = coordinates.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.MultiPolygon, isEmpty); if (!isEmpty) { const polygons = coordinates; writer.writeVarInt(polygons.length); const previousPoint = [0, 0, 0, 0]; for (const polygonRings of polygons) { writer.writeVarInt(polygonRings.length); for (const ring of polygonRings) { writer.writeVarInt(ring.length); for (const point of ring) { writeTwkbPoint(writer, context, previousPoint, point); } } } } } function encodeGeometryCollection(writer, context, geometryCollection) { const { geometries } = geometryCollection; const isEmpty = geometries.length === 0; writeTwkbHeader(writer, context, WKBGeometryType.GeometryCollection, isEmpty); if (geometries.length > 0) { writer.writeVarInt(geometries.length); for (const geometry of geometries) { encodeGeometry(writer, geometry, context); } } } /** * * @param writer * @param context * @param geometryType * @param isEmpty */ function writeTwkbHeader(writer, context, geometryType, isEmpty) { const type = (zigZagEncode(context.xy) << 4) + geometryType; let metadataHeader = context.hasZ || context.hasM ? 1 << 3 : 0; metadataHeader += isEmpty ? 1 << 4 : 0; writer.writeUInt8(type); writer.writeUInt8(metadataHeader); if (context.hasZ || context.hasM) { let extendedPrecision = 0; if (context.hasZ) { extendedPrecision |= 0x1; } if (context.hasM) { extendedPrecision |= 0x2; } writer.writeUInt8(extendedPrecision); } } /** * Write one point to array buffer. ZigZagEncoding the delta fdrom the previous point. Mutates previousPoint. * @param writer * @param context * @param previousPoint - Mutated by this function * @param point */ function writeTwkbPoint(writer, context, point, previousPoint) { const x = point[0] * context.xyFactor; const y = point[1] * context.xyFactor; const z = point[2] * context.zFactor; const m = point[3] * context.mFactor; writer.writeVarInt(zigZagEncode(x - previousPoint[0])); writer.writeVarInt(zigZagEncode(y - previousPoint[1])); if (context.hasZ) { writer.writeVarInt(zigZagEncode(z - previousPoint[2])); } if (context.hasM) { writer.writeVarInt(zigZagEncode(m - previousPoint[3])); } previousPoint[0] = x; previousPoint[1] = y; previousPoint[2] = z; previousPoint[3] = m; } // HELPERS function zigZagEncode(value) { return (value << 1) ^ (value >> 31); } function getTwkbPrecision(xyPrecision, zPrecision, mPrecision) { return { xy: xyPrecision, z: zPrecision, m: mPrecision, xyFactor: Math.pow(10, xyPrecision), zFactor: Math.pow(10, zPrecision), mFactor: Math.pow(10, mPrecision) }; }