@loaders.gl/wkt
Version:
Loader and Writer for the WKT (Well Known Text) Format
1,556 lines (1,533 loc) • 63.5 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if (typeof define === 'function' && define.amd) define([], factory);
else if (typeof exports === 'object') exports['loaders'] = factory();
else root['loaders'] = factory();})(globalThis, function () {
"use strict";
var __exports__ = (() => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// external-global-plugin:@loaders.gl/core
var require_core = __commonJS({
"external-global-plugin:@loaders.gl/core"(exports, module) {
module.exports = globalThis.loaders;
}
});
// bundle.ts
var bundle_exports = {};
__export(bundle_exports, {
HexWKBLoader: () => HexWKBLoader,
TWKBLoader: () => TWKBLoader,
TWKBWriter: () => TWKBWriter,
WKBLoader: () => WKBLoader,
WKBWorkerLoader: () => WKBWorkerLoader,
WKBWriter: () => WKBWriter,
WKTCRSLoader: () => WKTCRSLoader,
WKTCRSWriter: () => WKTCRSWriter,
WKTLoader: () => WKTLoader,
WKTWorkerLoader: () => WKTWorkerLoader,
WKTWriter: () => WKTWriter,
decodeHex: () => decodeHex,
encodeHex: () => encodeHex,
isTWKB: () => isTWKB,
isWKB: () => isWKB,
isWKT: () => isWKT,
parseWKBHeader: () => parseWKBHeader
});
__reExport(bundle_exports, __toESM(require_core(), 1));
// src/lib/utils/version.ts
var VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : "latest";
// src/lib/parse-wkt-crs.ts
function parseWKTCRS(wkt, options) {
if (options?.debug) {
console.log("[wktcrs] parse starting with\n", wkt);
}
wkt = wkt.replace(/[A-Z][A-Z\d_]+\[/gi, (match) => `["${match.substr(0, match.length - 1)}",`);
wkt = wkt.replace(/, ?([A-Z][A-Z\d_]+[,\]])/gi, (match, p1) => {
const varname = p1.substr(0, p1.length - 1);
return `,"${options?.raw ? "raw:" : ""}${varname}"${p1[p1.length - 1]}`;
});
if (options?.raw) {
wkt = wkt.replace(/, {0,2}(-?[\.\d]+)(?=,|\])/g, function(match, p1) {
return `,"${options?.raw ? "raw:" : ""}${p1}"`;
});
}
if (options?.debug) {
console.log(`[wktcrs] json'd wkt: '${wkt}'`);
}
let data;
try {
data = JSON.parse(wkt);
} catch (error) {
console.error(`[wktcrs] failed to parse '${wkt}'`);
throw error;
}
if (options?.debug) {
console.log(`[wktcrs] json parsed: '${wkt}'`);
}
function process(data2, parent) {
const kw = data2[0];
data2.forEach(function(it) {
if (Array.isArray(it)) {
process(it, data2);
}
});
const kwarr = `MULTIPLE_${kw}`;
if (kwarr in parent) {
parent[kwarr].push(data2);
} else if (kw in parent) {
parent[kwarr] = [parent[kw], data2];
delete parent[kw];
} else {
parent[kw] = data2;
}
return parent;
}
const result = process(data, [data]);
if (options?.debug) {
console.log("[wktcrs] parse returning", result);
}
if (options?.sort) {
sort(result, options);
}
return result;
}
function sort(data, options) {
const keys = Object.keys(data).filter((k) => !/\d+/.test(k));
const keywords = options?.keywords || [];
if (!options?.keywords) {
const counts = {};
if (Array.isArray(data)) {
data.forEach((it) => {
if (Array.isArray(it) && it.length >= 2 && typeof it[1] === "string") {
const k = it[0];
if (!counts[k])
counts[k] = 0;
counts[k]++;
}
});
for (const k in counts) {
if (counts[k] > 0)
keywords.push(k);
}
}
}
keys.forEach((key) => {
data[key] = sort(data[key]);
});
keywords.forEach((key) => {
const indices = [];
const params = [];
data.forEach((item, i) => {
if (Array.isArray(item) && item[0] === key) {
indices.push(i);
params.push(item);
}
});
params.sort((a, b) => {
a = a[1].toString();
b = b[1].toString();
return a < b ? -1 : a > b ? 1 : 0;
});
params.forEach((param, i) => {
data[indices[i]] = param;
});
});
return data;
}
// src/wkt-crs-loader.ts
var WKTCRSLoader = {
dataType: null,
batchType: null,
name: "WKT CRS (Well-Known Text Coordinate Reference System)",
id: "wkt-crs",
module: "wkt-crs",
version: VERSION,
worker: true,
extensions: [],
mimeTypes: ["text/plain"],
category: "json",
text: true,
options: {
"wkt-crs": {}
},
parse: async (arrayBuffer, options) => parseWKTCRS(new TextDecoder().decode(arrayBuffer), options?.["wkt-crs"]),
parseTextSync: (string, options) => parseWKTCRS(string, options?.["wkt-crs"])
};
// src/lib/encode-wkt-crs.ts
function encodeWKTCRS(wkt, options) {
if (Array.isArray(wkt) && wkt.length === 1 && Array.isArray(wkt[0])) {
wkt = wkt[0];
}
const [kw, ...attrs] = wkt;
const str = `${kw}[${attrs.map((attr) => {
if (Array.isArray(attr)) {
return encodeWKTCRS(attr, options);
} else if (typeof attr === "number") {
return attr.toString();
} else if (typeof attr === "string") {
if (attr.startsWith("raw:")) {
return attr.replace("raw:", "");
}
return `"${attr}"`;
}
throw new Error(`[wktcrs] unexpected attribute "${attr}"`);
}).join(",")}]`;
return str;
}
// src/wkt-crs-writer.ts
var WKTCRSWriter = {
name: "WKT CRS (Well-Known Text Coordinate Reference System)",
id: "wkt-crs",
module: "wkt-crs",
version: VERSION,
worker: true,
extensions: [],
mimeTypes: ["text/plain"],
// category: 'json',
text: true,
options: {
"wkt-crs": {}
},
encode: async (wktcrs, options) => new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.["wkt-crs"])),
encodeSync: (wktcrs, options) => new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.["wkt-crs"])),
encodeTextSync: (wktcrs, options) => encodeWKTCRS(wktcrs, options?.["wkt-crs"])
};
// src/lib/parse-wkt.ts
var numberRegexp = /[-+]?([0-9]*\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/;
var tuples = new RegExp("^" + numberRegexp.source + "(\\s" + numberRegexp.source + "){1,}");
var WKT_MAGIC_STRINGS = [
"POINT(",
"LINESTRING(",
"POLYGON(",
"MULTIPOINT(",
"MULTILINESTRING(",
"MULTIPOLYGON(",
"GEOMETRYCOLLECTION("
// We only support this "geojson" subset of the OGC simple features standard
];
function isWKT(input) {
return WKT_MAGIC_STRINGS.some((magicString) => input.startsWith(magicString));
}
function parseWKT(input, options) {
return parseWKTToGeometry(input, options);
}
function parseWKTToGeometry(input, options) {
const parts = input.split(";");
let _ = parts.pop();
const srid = (parts.shift() || "").split("=").pop();
const state = { parts, _, i: 0 };
const geometry = parseGeometry(state);
return options?.wkt?.crs ? addCRS(geometry, srid) : geometry;
}
function parseGeometry(state) {
return parsePoint(state) || parseLineString(state) || parsePolygon(state) || parseMultiPoint(state) || parseMultiLineString(state) || parseMultiPolygon(state) || parseGeometryCollection(state);
}
function addCRS(obj, srid) {
if (obj && srid?.match(/\d+/)) {
const crs = {
type: "name",
properties: {
name: "urn:ogc:def:crs:EPSG::" + srid
}
};
obj.crs = crs;
}
return obj;
}
function parsePoint(state) {
if (!$(/^(POINT(\sz)?)/i, state)) {
return null;
}
white(state);
if (!$(/^(\()/, state)) {
return null;
}
const c = coords(state);
if (!c) {
return null;
}
white(state);
if (!$(/^(\))/, state)) {
return null;
}
return {
type: "Point",
coordinates: c[0]
};
}
function parseMultiPoint(state) {
if (!$(/^(MULTIPOINT)/i, state)) {
return null;
}
white(state);
const newCoordsFormat = state._?.substring(state._?.indexOf("(") + 1, state._.length - 1).replace(/\(/g, "").replace(/\)/g, "");
state._ = "MULTIPOINT (" + newCoordsFormat + ")";
const c = multicoords(state);
if (!c) {
return null;
}
white(state);
return {
type: "MultiPoint",
coordinates: c
};
}
function parseLineString(state) {
if (!$(/^(LINESTRING(\sz)?)/i, state)) {
return null;
}
white(state);
if (!$(/^(\()/, state)) {
return null;
}
const c = coords(state);
if (!c) {
return null;
}
if (!$(/^(\))/, state)) {
return null;
}
return {
type: "LineString",
coordinates: c
};
}
function parseMultiLineString(state) {
if (!$(/^(MULTILINESTRING)/i, state))
return null;
white(state);
const c = multicoords(state);
if (!c) {
return null;
}
white(state);
return {
// @ts-ignore
type: "MultiLineString",
// @ts-expect-error
coordinates: c
};
}
function parsePolygon(state) {
if (!$(/^(POLYGON(\sz)?)/i, state)) {
return null;
}
white(state);
const c = multicoords(state);
if (!c) {
return null;
}
return {
// @ts-ignore
type: "Polygon",
// @ts-expect-error
coordinates: c
};
}
function parseMultiPolygon(state) {
if (!$(/^(MULTIPOLYGON)/i, state)) {
return null;
}
white(state);
const c = multicoords(state);
if (!c) {
return null;
}
return {
type: "MultiPolygon",
// @ts-expect-error
coordinates: c
};
}
function parseGeometryCollection(state) {
const geometries = [];
let geometry;
if (!$(/^(GEOMETRYCOLLECTION)/i, state)) {
return null;
}
white(state);
if (!$(/^(\()/, state)) {
return null;
}
while (geometry = parseGeometry(state)) {
geometries.push(geometry);
white(state);
$(/^(,)/, state);
white(state);
}
if (!$(/^(\))/, state)) {
return null;
}
return {
type: "GeometryCollection",
geometries
};
}
function multicoords(state) {
white(state);
let depth = 0;
const rings = [];
const stack = [rings];
let pointer = rings;
let elem;
while (elem = $(/^(\()/, state) || $(/^(\))/, state) || $(/^(,)/, state) || $(tuples, state)) {
if (elem === "(") {
stack.push(pointer);
pointer = [];
stack[stack.length - 1].push(pointer);
depth++;
} else if (elem === ")") {
if (pointer.length === 0)
return null;
pointer = stack.pop();
if (!pointer)
return null;
depth--;
if (depth === 0)
break;
} else if (elem === ",") {
pointer = [];
stack[stack.length - 1].push(pointer);
} else if (!elem.split(/\s/g).some(isNaN)) {
Array.prototype.push.apply(pointer, elem.split(/\s/g).map(parseFloat));
} else {
return null;
}
white(state);
}
if (depth !== 0)
return null;
return rings;
}
function coords(state) {
const list = [];
let item;
let pt;
while (pt = $(tuples, state) || $(/^(,)/, state)) {
if (pt === ",") {
list.push(item);
item = [];
} else if (!pt.split(/\s/g).some(isNaN)) {
if (!item)
item = [];
Array.prototype.push.apply(item, pt.split(/\s/g).map(parseFloat));
}
white(state);
}
if (item)
list.push(item);
else
return null;
return list.length ? list : null;
}
function $(regexp, state) {
const match = state._?.substring(state.i).match(regexp);
if (!match)
return null;
else {
state.i += match[0].length;
return match[0];
}
}
function white(state) {
$(/^\s*/, state);
}
// src/wkt-loader.ts
var WKTWorkerLoader = {
dataType: null,
batchType: null,
name: "WKT (Well-Known Text)",
id: "wkt",
module: "wkt",
version: VERSION,
worker: true,
extensions: ["wkt"],
mimeTypes: ["text/plain"],
category: "geometry",
text: true,
tests: WKT_MAGIC_STRINGS,
testText: isWKT,
options: {
wkt: {
shape: "geojson-geometry",
crs: true
}
}
};
var WKTLoader = {
...WKTWorkerLoader,
parse: async (arrayBuffer, options) => parseWKT(new TextDecoder().decode(arrayBuffer), options),
parseTextSync: (string, options) => parseWKT(string, options)
};
// src/lib/encode-wkt.ts
function encodeWKT(geometry) {
if (geometry.type === "Feature") {
geometry = geometry.geometry;
}
switch (geometry.type) {
case "Point":
return `POINT ${wrapParens(pairWKT(geometry.coordinates))}`;
case "LineString":
return `LINESTRING ${wrapParens(ringWKT(geometry.coordinates))}`;
case "Polygon":
return `POLYGON ${wrapParens(ringsWKT(geometry.coordinates))}`;
case "MultiPoint":
return `MULTIPOINT ${wrapParens(ringWKT(geometry.coordinates))}`;
case "MultiPolygon":
return `MULTIPOLYGON ${wrapParens(multiRingsWKT(geometry.coordinates))}`;
case "MultiLineString":
return `MULTILINESTRING ${wrapParens(ringsWKT(geometry.coordinates))}`;
case "GeometryCollection":
return `GEOMETRYCOLLECTION ${wrapParens(geometry.geometries.map(encodeWKT).join(", "))}`;
default:
throw new Error("stringify requires a valid GeoJSON Feature or geometry object as input");
}
}
function pairWKT(c) {
return c.join(" ");
}
function ringWKT(r) {
return r.map(pairWKT).join(", ");
}
function ringsWKT(r) {
return r.map(ringWKT).map(wrapParens).join(", ");
}
function multiRingsWKT(r) {
return r.map(ringsWKT).map(wrapParens).join(", ");
}
function wrapParens(s) {
return `(${s})`;
}
// src/wkt-writer.ts
var WKTWriter = {
name: "WKT (Well Known Text)",
id: "wkt",
module: "wkt",
version: VERSION,
extensions: ["wkt"],
text: true,
encode: async (geometry) => encodeWKTSync(geometry),
encodeSync: encodeWKTSync,
encodeTextSync: encodeWKT,
options: {
wkt: {}
}
};
function encodeWKTSync(geometry) {
return new TextEncoder().encode(encodeWKT(geometry)).buffer;
}
// ../gis/src/lib/binary-features/binary-to-geojson.ts
function binaryToGeometry(data, startIndex, endIndex) {
switch (data.type) {
case "Point":
return pointToGeoJson(data, startIndex, endIndex);
case "LineString":
return lineStringToGeoJson(data, startIndex, endIndex);
case "Polygon":
return polygonToGeoJson(data, startIndex, endIndex);
default:
const unexpectedInput = data;
throw new Error(`Unsupported geometry type: ${unexpectedInput?.type}`);
}
}
function polygonToGeoJson(data, startIndex = -Infinity, endIndex = Infinity) {
const { positions } = data;
const polygonIndices = data.polygonIndices.value.filter((x) => x >= startIndex && x <= endIndex);
const primitivePolygonIndices = data.primitivePolygonIndices.value.filter(
(x) => x >= startIndex && x <= endIndex
);
const multi = polygonIndices.length > 2;
if (!multi) {
const coordinates2 = [];
for (let i = 0; i < primitivePolygonIndices.length - 1; i++) {
const startRingIndex = primitivePolygonIndices[i];
const endRingIndex = primitivePolygonIndices[i + 1];
const ringCoordinates = ringToGeoJson(positions, startRingIndex, endRingIndex);
coordinates2.push(ringCoordinates);
}
return { type: "Polygon", coordinates: coordinates2 };
}
const coordinates = [];
for (let i = 0; i < polygonIndices.length - 1; i++) {
const startPolygonIndex = polygonIndices[i];
const endPolygonIndex = polygonIndices[i + 1];
const polygonCoordinates = polygonToGeoJson(
data,
startPolygonIndex,
endPolygonIndex
).coordinates;
coordinates.push(polygonCoordinates);
}
return { type: "MultiPolygon", coordinates };
}
function lineStringToGeoJson(data, startIndex = -Infinity, endIndex = Infinity) {
const { positions } = data;
const pathIndices = data.pathIndices.value.filter((x) => x >= startIndex && x <= endIndex);
const multi = pathIndices.length > 2;
if (!multi) {
const coordinates2 = ringToGeoJson(positions, pathIndices[0], pathIndices[1]);
return { type: "LineString", coordinates: coordinates2 };
}
const coordinates = [];
for (let i = 0; i < pathIndices.length - 1; i++) {
const ringCoordinates = ringToGeoJson(positions, pathIndices[i], pathIndices[i + 1]);
coordinates.push(ringCoordinates);
}
return { type: "MultiLineString", coordinates };
}
function pointToGeoJson(data, startIndex, endIndex) {
const { positions } = data;
const coordinates = ringToGeoJson(positions, startIndex, endIndex);
const multi = coordinates.length > 1;
if (multi) {
return { type: "MultiPoint", coordinates };
}
return { type: "Point", coordinates: coordinates[0] };
}
function ringToGeoJson(positions, startIndex, endIndex) {
startIndex = startIndex || 0;
endIndex = endIndex || positions.value.length / positions.size;
const ringCoordinates = [];
for (let j = startIndex; j < endIndex; j++) {
const coord = Array();
for (let k = j * positions.size; k < (j + 1) * positions.size; k++) {
coord.push(Number(positions.value[k]));
}
ringCoordinates.push(coord);
}
return ringCoordinates;
}
// src/lib/parse-wkb-header.ts
var EWKB_FLAG_Z = 2147483648;
var EWKB_FLAG_M = 1073741824;
var EWKB_FLAG_SRID = 536870912;
var MAX_SRID = 1e4;
function isWKB(arrayBuffer) {
const dataView = new DataView(arrayBuffer);
let byteOffset = 0;
const endianness = dataView.getUint8(byteOffset);
byteOffset += 1;
if (endianness > 1) {
return false;
}
const littleEndian = endianness === 1;
const geometry = dataView.getUint32(byteOffset, littleEndian);
byteOffset += 4;
const geometryType = geometry & 7;
if (geometryType === 0 || geometryType > 7) {
return false;
}
const geometryFlags = geometry - geometryType;
if (geometryFlags === 0 || geometryFlags === 1e3 || geometryFlags === 2e3 || geometryFlags === 3e3) {
return true;
}
if ((geometryFlags & ~(EWKB_FLAG_Z | EWKB_FLAG_M | EWKB_FLAG_SRID)) !== 0) {
return false;
}
if (geometryFlags & EWKB_FLAG_SRID) {
const srid = dataView.getUint32(byteOffset, littleEndian);
byteOffset += 4;
if (srid > MAX_SRID) {
return false;
}
}
return true;
}
function parseWKBHeader(dataView, target) {
const wkbHeader = Object.assign(target || {}, {
type: "wkb",
geometryType: 1,
dimensions: 2,
coordinates: "xy",
littleEndian: true,
byteOffset: 0
});
wkbHeader.littleEndian = dataView.getUint8(wkbHeader.byteOffset) === 1;
wkbHeader.byteOffset++;
const geometryCode = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian);
wkbHeader.byteOffset += 4;
wkbHeader.geometryType = geometryCode & 7;
const isoType = (geometryCode - wkbHeader.geometryType) / 1e3;
switch (isoType) {
case 0:
break;
case 1:
wkbHeader.type = "iso-wkb";
wkbHeader.dimensions = 3;
wkbHeader.coordinates = "xyz";
break;
case 2:
wkbHeader.type = "iso-wkb";
wkbHeader.dimensions = 3;
wkbHeader.coordinates = "xym";
break;
case 3:
wkbHeader.type = "iso-wkb";
wkbHeader.dimensions = 4;
wkbHeader.coordinates = "xyzm";
break;
default:
throw new Error(`WKB: Unsupported iso-wkb type: ${isoType}`);
}
const ewkbZ = geometryCode & EWKB_FLAG_Z;
const ewkbM = geometryCode & EWKB_FLAG_M;
const ewkbSRID = geometryCode & EWKB_FLAG_SRID;
if (ewkbZ && ewkbM) {
wkbHeader.type = "ewkb";
wkbHeader.dimensions = 4;
wkbHeader.coordinates = "xyzm";
} else if (ewkbZ) {
wkbHeader.type = "ewkb";
wkbHeader.dimensions = 3;
wkbHeader.coordinates = "xyz";
} else if (ewkbM) {
wkbHeader.type = "ewkb";
wkbHeader.dimensions = 3;
wkbHeader.coordinates = "xym";
}
if (ewkbSRID) {
wkbHeader.type = "ewkb";
wkbHeader.srid = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian);
wkbHeader.byteOffset += 4;
}
return wkbHeader;
}
// src/lib/parse-wkb.ts
function parseWKB(arrayBuffer, options) {
const binaryGeometry = parseWKBToBinary(arrayBuffer, options);
const shape = options?.wkb?.shape || "binary-geometry";
switch (shape) {
case "binary-geometry":
return binaryGeometry;
case "geojson-geometry":
return binaryToGeometry(binaryGeometry);
case "geometry":
console.error('WKBLoader: "geometry" shape is deprecated, use "binary-geometry" instead');
return binaryToGeometry(binaryGeometry);
default:
throw new Error(shape);
}
}
function parseWKBToBinary(arrayBuffer, options) {
const dataView = new DataView(arrayBuffer);
const wkbHeader = parseWKBHeader(dataView);
const { geometryType, dimensions, littleEndian } = wkbHeader;
const offset = wkbHeader.byteOffset;
switch (geometryType) {
case 1 /* Point */:
const point = parsePoint2(dataView, offset, dimensions, littleEndian);
return point.geometry;
case 2 /* LineString */:
const line = parseLineString2(dataView, offset, dimensions, littleEndian);
return line.geometry;
case 3 /* Polygon */:
const polygon = parsePolygon2(dataView, offset, dimensions, littleEndian);
return polygon.geometry;
case 4 /* MultiPoint */:
const multiPoint = parseMultiPoint2(dataView, offset, dimensions, littleEndian);
multiPoint.type = "Point";
return multiPoint;
case 5 /* MultiLineString */:
const multiLine = parseMultiLineString2(dataView, offset, dimensions, littleEndian);
multiLine.type = "LineString";
return multiLine;
case 6 /* MultiPolygon */:
const multiPolygon = parseMultiPolygon2(dataView, offset, dimensions, littleEndian);
multiPolygon.type = "Polygon";
return multiPolygon;
default:
throw new Error(`WKB: Unsupported geometry type: ${geometryType}`);
}
}
function parsePoint2(dataView, offset, dimension, littleEndian) {
const positions = new Float64Array(dimension);
for (let i = 0; i < dimension; i++) {
positions[i] = dataView.getFloat64(offset, littleEndian);
offset += 8;
}
return {
geometry: { type: "Point", positions: { value: positions, size: dimension } },
offset
};
}
function parseLineString2(dataView, offset, dimension, littleEndian) {
const nPoints = dataView.getUint32(offset, littleEndian);
offset += 4;
const positions = new Float64Array(nPoints * dimension);
for (let i = 0; i < nPoints * dimension; i++) {
positions[i] = dataView.getFloat64(offset, littleEndian);
offset += 8;
}
const pathIndices = [0];
if (nPoints > 0) {
pathIndices.push(nPoints);
}
return {
geometry: {
type: "LineString",
positions: { value: positions, size: dimension },
pathIndices: { value: new Uint32Array(pathIndices), size: 1 }
},
offset
};
}
var cumulativeSum = (sum) => (value) => sum += value;
function parsePolygon2(dataView, offset, dimension, littleEndian) {
const nRings = dataView.getUint32(offset, littleEndian);
offset += 4;
const rings = [];
for (let i = 0; i < nRings; i++) {
const parsed = parseLineString2(dataView, offset, dimension, littleEndian);
const { positions } = parsed.geometry;
offset = parsed.offset;
rings.push(positions.value);
}
const concatenatedPositions = new Float64Array(concatTypedArrays(rings).buffer);
const polygonIndices = [0];
if (concatenatedPositions.length > 0) {
polygonIndices.push(concatenatedPositions.length / dimension);
}
const primitivePolygonIndices = rings.map((l) => l.length / dimension).map(cumulativeSum(0));
primitivePolygonIndices.unshift(0);
return {
geometry: {
type: "Polygon",
positions: { value: concatenatedPositions, size: dimension },
polygonIndices: {
value: new Uint32Array(polygonIndices),
size: 1
},
primitivePolygonIndices: { value: new Uint32Array(primitivePolygonIndices), size: 1 }
},
offset
};
}
function parseMultiPoint2(dataView, offset, dimension, littleEndian) {
const nPoints = dataView.getUint32(offset, littleEndian);
offset += 4;
const binaryPointGeometries = [];
for (let i = 0; i < nPoints; i++) {
const littleEndianPoint = dataView.getUint8(offset) === 1;
offset++;
if (dataView.getUint32(offset, littleEndianPoint) % 1e3 !== 1) {
throw new Error("WKB: Inner geometries of MultiPoint not of type Point");
}
offset += 4;
const parsed = parsePoint2(dataView, offset, dimension, littleEndianPoint);
offset = parsed.offset;
binaryPointGeometries.push(parsed.geometry);
}
return concatenateBinaryPointGeometries(binaryPointGeometries, dimension);
}
function parseMultiLineString2(dataView, offset, dimension, littleEndian) {
const nLines = dataView.getUint32(offset, littleEndian);
offset += 4;
const binaryLineGeometries = [];
for (let i = 0; i < nLines; i++) {
const littleEndianLine = dataView.getUint8(offset) === 1;
offset++;
if (dataView.getUint32(offset, littleEndianLine) % 1e3 !== 2) {
throw new Error("WKB: Inner geometries of MultiLineString not of type LineString");
}
offset += 4;
const parsed = parseLineString2(dataView, offset, dimension, littleEndianLine);
offset = parsed.offset;
binaryLineGeometries.push(parsed.geometry);
}
return concatenateBinaryLineGeometries(binaryLineGeometries, dimension);
}
function parseMultiPolygon2(dataView, offset, dimension, littleEndian) {
const nPolygons = dataView.getUint32(offset, littleEndian);
offset += 4;
const binaryPolygonGeometries = [];
for (let i = 0; i < nPolygons; i++) {
const littleEndianPolygon = dataView.getUint8(offset) === 1;
offset++;
if (dataView.getUint32(offset, littleEndianPolygon) % 1e3 !== 3) {
throw new Error("WKB: Inner geometries of MultiPolygon not of type Polygon");
}
offset += 4;
const parsed = parsePolygon2(dataView, offset, dimension, littleEndianPolygon);
offset = parsed.offset;
binaryPolygonGeometries.push(parsed.geometry);
}
return concatenateBinaryPolygonGeometries(binaryPolygonGeometries, dimension);
}
function concatenateBinaryPointGeometries(binaryPointGeometries, dimension) {
const positions = binaryPointGeometries.map((geometry) => geometry.positions.value);
const concatenatedPositions = new Float64Array(concatTypedArrays(positions).buffer);
return {
type: "Point",
positions: { value: concatenatedPositions, size: dimension }
};
}
function concatenateBinaryLineGeometries(binaryLineGeometries, dimension) {
const lines = binaryLineGeometries.map((geometry) => geometry.positions.value);
const concatenatedPositions = new Float64Array(concatTypedArrays(lines).buffer);
const pathIndices = lines.map((line) => line.length / dimension).map(cumulativeSum(0));
pathIndices.unshift(0);
return {
type: "LineString",
positions: { value: concatenatedPositions, size: dimension },
pathIndices: { value: new Uint32Array(pathIndices), size: 1 }
};
}
function concatenateBinaryPolygonGeometries(binaryPolygonGeometries, dimension) {
const polygons = [];
const primitivePolygons = [];
for (const binaryPolygon of binaryPolygonGeometries) {
const { positions, primitivePolygonIndices: primitivePolygonIndices2 } = binaryPolygon;
polygons.push(positions.value);
primitivePolygons.push(primitivePolygonIndices2.value);
}
const concatenatedPositions = new Float64Array(concatTypedArrays(polygons).buffer);
const polygonIndices = polygons.map((p) => p.length / dimension).map(cumulativeSum(0));
polygonIndices.unshift(0);
const primitivePolygonIndices = [0];
for (const primitivePolygon of primitivePolygons) {
primitivePolygonIndices.push(
...primitivePolygon.filter((x) => x > 0).map((x) => x + primitivePolygonIndices[primitivePolygonIndices.length - 1])
);
}
return {
type: "Polygon",
positions: { value: concatenatedPositions, size: dimension },
polygonIndices: { value: new Uint32Array(polygonIndices), size: 1 },
primitivePolygonIndices: { value: new Uint32Array(primitivePolygonIndices), size: 1 }
};
}
function concatTypedArrays(arrays) {
let byteLength = 0;
for (let i = 0; i < arrays.length; ++i) {
byteLength += arrays[i].byteLength;
}
const buffer = new Uint8Array(byteLength);
let byteOffset = 0;
for (let i = 0; i < arrays.length; ++i) {
const data = new Uint8Array(arrays[i].buffer);
byteLength = data.length;
for (let j = 0; j < byteLength; ++j) {
buffer[byteOffset++] = data[j];
}
}
return buffer;
}
// src/wkb-loader.ts
var WKBWorkerLoader = {
dataType: null,
batchType: null,
name: "WKB",
id: "wkb",
module: "wkt",
version: VERSION,
worker: true,
category: "geometry",
extensions: ["wkb"],
mimeTypes: [],
// TODO can we define static, serializable tests, eg. some binary strings?
tests: [isWKB],
options: {
wkb: {
shape: "binary-geometry"
// 'geojson-geometry'
}
}
};
var WKBLoader = {
...WKBWorkerLoader,
parse: async (arrayBuffer) => parseWKB(arrayBuffer),
parseSync: parseWKB
};
// src/lib/utils/binary-writer.ts
var LE = true;
var BE = false;
var BinaryWriter = class {
arrayBuffer;
dataView;
byteOffset = 0;
allowResize = false;
constructor(size, allowResize) {
this.arrayBuffer = new ArrayBuffer(size);
this.dataView = new DataView(this.arrayBuffer);
this.byteOffset = 0;
this.allowResize = allowResize || false;
}
writeUInt8(value) {
this._ensureSize(1);
this.dataView.setUint8(this.byteOffset, value);
this.byteOffset += 1;
}
writeUInt16LE(value) {
this._ensureSize(2);
this.dataView.setUint16(this.byteOffset, value, LE);
this.byteOffset += 2;
}
writeUInt16BE(value) {
this._ensureSize(2);
this.dataView.setUint16(this.byteOffset, value, BE);
this.byteOffset += 2;
}
writeUInt32LE(value) {
this._ensureSize(4);
this.dataView.setUint32(this.byteOffset, value, LE);
this.byteOffset += 4;
}
writeUInt32BE(value) {
this._ensureSize(4);
this.dataView.setUint32(this.byteOffset, value, BE);
this.byteOffset += 4;
}
writeInt8(value) {
this._ensureSize(1);
this.dataView.setInt8(this.byteOffset, value);
this.byteOffset += 1;
}
writeInt16LE(value) {
this._ensureSize(2);
this.dataView.setInt16(this.byteOffset, value, LE);
this.byteOffset += 2;
}
writeInt16BE(value) {
this._ensureSize(2);
this.dataView.setInt16(this.byteOffset, value, BE);
this.byteOffset += 2;
}
writeInt32LE(value) {
this._ensureSize(4);
this.dataView.setInt32(this.byteOffset, value, LE);
this.byteOffset += 4;
}
writeInt32BE(value) {
this._ensureSize(4);
this.dataView.setInt32(this.byteOffset, value, BE);
this.byteOffset += 4;
}
writeFloatLE(value) {
this._ensureSize(4);
this.dataView.setFloat32(this.byteOffset, value, LE);
this.byteOffset += 4;
}
writeFloatBE(value) {
this._ensureSize(4);
this.dataView.setFloat32(this.byteOffset, value, BE);
this.byteOffset += 4;
}
writeDoubleLE(value) {
this._ensureSize(8);
this.dataView.setFloat64(this.byteOffset, value, LE);
this.byteOffset += 8;
}
writeDoubleBE(value) {
this._ensureSize(8);
this.dataView.setFloat64(this.byteOffset, value, BE);
this.byteOffset += 8;
}
/** A varint uses a variable number of bytes */
writeVarInt(value) {
let length = 1;
while ((value & 4294967168) !== 0) {
this.writeUInt8(value & 127 | 128);
value >>>= 7;
length++;
}
this.writeUInt8(value & 127);
return length;
}
/** Append another ArrayBuffer to this ArrayBuffer */
writeBuffer(arrayBuffer) {
this._ensureSize(arrayBuffer.byteLength);
const tempArray = new Uint8Array(this.arrayBuffer);
tempArray.set(new Uint8Array(arrayBuffer), this.byteOffset);
this.byteOffset += arrayBuffer.byteLength;
}
/** Resizes this.arrayBuffer if not enough space */
_ensureSize(size) {
if (this.arrayBuffer.byteLength < this.byteOffset + size) {
if (this.allowResize) {
const newArrayBuffer = new ArrayBuffer(this.byteOffset + size);
const tempArray = new Uint8Array(newArrayBuffer);
tempArray.set(new Uint8Array(this.arrayBuffer));
this.arrayBuffer = newArrayBuffer;
} else {
throw new Error("BinaryWriter overflow");
}
}
}
};
// src/lib/encode-wkb.ts
function encodeWKB(geometry, options = {}) {
if (geometry.type === "Feature") {
geometry = geometry.geometry;
}
switch (geometry.type) {
case "Point":
return encodePoint(geometry.coordinates, options);
case "LineString":
return encodeLineString(geometry.coordinates, options);
case "Polygon":
return encodePolygon(geometry.coordinates, options);
case "MultiPoint":
return encodeMultiPoint(geometry, options);
case "MultiPolygon":
return encodeMultiPolygon(geometry, options);
case "MultiLineString":
return encodeMultiLineString(geometry, options);
case "GeometryCollection":
return encodeGeometryCollection(geometry, options);
default:
const exhaustiveCheck = geometry;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
function getGeometrySize(geometry, options) {
switch (geometry.type) {
case "Point":
return getPointSize(options);
case "LineString":
return getLineStringSize(geometry.coordinates, options);
case "Polygon":
return getPolygonSize(geometry.coordinates, options);
case "MultiPoint":
return getMultiPointSize(geometry, options);
case "MultiPolygon":
return getMultiPolygonSize(geometry, options);
case "MultiLineString":
return getMultiLineStringSize(geometry, options);
case "GeometryCollection":
return getGeometryCollectionSize(geometry, options);
default:
const exhaustiveCheck = geometry;
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
}
}
function encodePoint(coordinates, options) {
const writer = new BinaryWriter(getPointSize(options));
writer.writeInt8(1);
writeWkbType(writer, 1 /* Point */, options);
if (typeof coordinates[0] === "undefined" && typeof coordinates[1] === "undefined") {
writer.writeDoubleLE(NaN);
writer.writeDoubleLE(NaN);
if (options.hasZ) {
writer.writeDoubleLE(NaN);
}
if (options.hasM) {
writer.writeDoubleLE(NaN);
}
} else {
writeCoordinate(writer, coordinates, options);
}
return writer.arrayBuffer;
}
function writeCoordinate(writer, coordinate, options) {
writer.writeDoubleLE(coordinate[0]);
writer.writeDoubleLE(coordinate[1]);
if (options.hasZ) {
writer.writeDoubleLE(coordinate[2]);
}
if (options.hasM) {
writer.writeDoubleLE(coordinate[3]);
}
}
function getPointSize(options) {
const coordinateSize = getCoordinateSize(options);
return 1 + 4 + coordinateSize;
}
function encodeLineString(coordinates, options) {
const size = getLineStringSize(coordinates, options);
const writer = new BinaryWriter(size);
writer.writeInt8(1);
writeWkbType(writer, 2 /* LineString */, options);
writer.writeUInt32LE(coordinates.length);
for (const coordinate of coordinates) {
writeCoordinate(writer, coordinate, options);
}
return writer.arrayBuffer;
}
function getLineStringSize(coordinates, options) {
const coordinateSize = getCoordinateSize(options);
return 1 + 4 + 4 + coordinates.length * coordinateSize;
}
function encodePolygon(coordinates, options) {
const writer = new BinaryWriter(getPolygonSize(coordinates, options));
writer.writeInt8(1);
writeWkbType(writer, 3 /* Polygon */, options);
const [exteriorRing, ...interiorRings] = coordinates;
if (exteriorRing.length > 0) {
writer.writeUInt32LE(1 + interiorRings.length);
writer.writeUInt32LE(exteriorRing.length);
} else {
writer.writeUInt32LE(0);
}
for (const coordinate of exteriorRing) {
writeCoordinate(writer, coordinate, options);
}
for (const interiorRing of interiorRings) {
writer.writeUInt32LE(interiorRing.length);
for (const coordinate of interiorRing) {
writeCoordinate(writer, coordinate, options);
}
}
return writer.arrayBuffer;
}
function getPolygonSize(coordinates, options) {
const coordinateSize = getCoordinateSize(options);
const [exteriorRing, ...interiorRings] = coordinates;
let size = 1 + 4 + 4;
if (exteriorRing.length > 0) {
size += 4 + exteriorRing.length * coordinateSize;
}
for (const interiorRing of interiorRings) {
size += 4 + interiorRing.length * coordinateSize;
}
return size;
}
function encodeMultiPoint(multiPoint, options) {
const writer = new BinaryWriter(getMultiPointSize(multiPoint, options));
const points = multiPoint.coordinates;
writer.writeInt8(1);
writeWkbType(writer, 4 /* MultiPoint */, options);
writer.writeUInt32LE(points.length);
for (const point of points) {
const arrayBuffer = encodePoint(point, options);
writer.writeBuffer(arrayBuffer);
}
return writer.arrayBuffer;
}
function getMultiPointSize(multiPoint, options) {
let coordinateSize = getCoordinateSize(options);
const points = multiPoint.coordinates;
coordinateSize += 5;
return 1 + 4 + 4 + points.length * coordinateSize;
}
function encodeMultiLineString(multiLineString, options) {
const writer = new BinaryWriter(getMultiLineStringSize(multiLineString, options));
const lineStrings = multiLineString.coordinates;
writer.writeInt8(1);
writeWkbType(writer, 5 /* MultiLineString */, options);
writer.writeUInt32LE(lineStrings.length);
for (const lineString of lineStrings) {
const encodedLineString = encodeLineString(lineString, options);
writer.writeBuffer(encodedLineString);
}
return writer.arrayBuffer;
}
function getMultiLineStringSize(multiLineString, options) {
let size = 1 + 4 + 4;
const lineStrings = multiLineString.coordinates;
for (const lineString of lineStrings) {
size += getLineStringSize(lineString, options);
}
return size;
}
function encodeMultiPolygon(multiPolygon, options) {
const writer = new BinaryWriter(getMultiPolygonSize(multiPolygon, options));
const polygons = multiPolygon.coordinates;
writer.writeInt8(1);
writeWkbType(writer, 6 /* MultiPolygon */, options);
writer.writeUInt32LE(polygons.length);
for (const polygon of polygons) {
const encodedPolygon = encodePolygon(polygon, options);
writer.writeBuffer(encodedPolygon);
}
return writer.arrayBuffer;
}
function getMultiPolygonSize(multiPolygon, options) {
let size = 1 + 4 + 4;
const polygons = multiPolygon.coordinates;
for (const polygon of polygons) {
size += getPolygonSize(polygon, options);
}
return size;
}
function encodeGeometryCollection(collection, options) {
const writer = new BinaryWriter(getGeometryCollectionSize(collection, options));
writer.writeInt8(1);
writeWkbType(writer, 7 /* GeometryCollection */, options);
writer.writeUInt32LE(collection.geometries.length);
for (const geometry of collection.geometries) {
const arrayBuffer = encodeWKB(geometry, options);
writer.writeBuffer(arrayBuffer);
}
return writer.arrayBuffer;
}
function getGeometryCollectionSize(collection, options) {
let size = 1 + 4 + 4;
for (const geometry of collection.geometries) {
size += getGeometrySize(geometry, options);
}
return size;
}
function writeWkbType(writer, geometryType, options) {
const { hasZ, hasM, srid } = options;
let dimensionType = 0;
if (!srid) {
if (hasZ && hasM) {
dimensionType += 3e3;
} else if (hasZ) {
dimensionType += 1e3;
} else if (hasM) {
dimensionType += 2e3;
}
} else {
if (hasZ) {
dimensionType |= 2147483648;
}
if (hasM) {
dimensionType |= 1073741824;
}
}
writer.writeUInt32LE(dimensionType + geometryType >>> 0);
}
function getCoordinateSize(options) {
let coordinateSize = 16;
if (options.hasZ) {
coordinateSize += 8;
}
if (options.hasM) {
coordinateSize += 8;
}
return coordinateSize;
}
// src/wkb-writer.ts
var WKBWriter = {
name: "WKB (Well Known Binary)",
id: "wkb",
module: "wkt",
version: VERSION,
extensions: ["wkb"],
options: {
wkb: {
hasZ: false,
hasM: false
}
},
async encode(data, options) {
return encodeWKB(data, options?.wkb);
},
encodeSync(data, options) {
return encodeWKB(data, options?.wkb);
}
};
// src/lib/utils/hex-transcoder.ts
var alphabet = "0123456789abcdef";
var encodeLookup = [];
var decodeLookup = [];
for (let i = 0; i < 256; i++) {
encodeLookup[i] = alphabet[i >> 4 & 15] + alphabet[i & 15];
if (i < 16) {
if (i < 10) {
decodeLookup[48 + i] = i;
} else {
decodeLookup[97 - 10 + i] = i;
}
}
}
function encodeHex(array) {
const length = array.length;
let string = "";
let i = 0;
while (i < length) {
string += encodeLookup[array[i++]];
}
return string;
}
function decodeHex(string) {
const sizeof = string.length >> 1;
const length = sizeof << 1;
const array = new Uint8Array(sizeof);
let n = 0;
let i = 0;
while (i < length) {
array[n++] = decodeLookup[string.charCodeAt(i++)] << 4 | decodeLookup[string.charCodeAt(i++)];
}
return array;
}
// src/hex-wkb-loader.ts
var HexWKBLoader = {
dataType: null,
batchType: null,
name: "Hexadecimal WKB",
id: "wkb",
module: "wkt",
version: VERSION,
worker: true,
category: "geometry",
extensions: ["wkb"],
mimeTypes: [],
options: WKBLoader.options,
text: true,
testText: isHexWKB,
// TODO - encoding here seems wasteful - extend hex transcoder?
parse: async (arrayBuffer) => parseHexWKB(new TextDecoder().decode(arrayBuffer)),
parseTextSync: parseHexWKB
};
function parseHexWKB(text, options) {
const uint8Array = decodeHex(text);
const binaryGeometry = WKBLoader.parseSync?.(uint8Array.buffer, options);
return binaryGeometry;
}
function isHexWKB(string) {
if (!string) {
return false;
}
if (string.length < 10 || string.length % 2 !== 0) {
return false;
}
if (!string.startsWith("00") && !string.startsWith("01")) {
return false;
}
return /^[0-9a-fA-F]+$/.test(string.slice(2));
}
// src/lib/utils/binary-reader.ts
var BinaryReader = class {
arrayBuffer;
dataView;
byteOffset;
littleEndian;
constructor(arrayBuffer, isBigEndian = false) {
this.arrayBuffer = arrayBuffer;
this.dataView = new DataView(arrayBuffer);
this.byteOffset = 0;
this.littleEndian = !isBigEndian;
}
readUInt8() {
const value = this.dataView.getUint8(this.byteOffset);
this.byteOffset += 1;
return value;
}
readUInt16() {
const value = this.dataView.getUint16(this.byteOffset, this.littleEndian);
this.byteOffset += 2;
return value;
}
readUInt32() {
const value = this.dataView.getUint32(this.byteOffset, this.littleEndian);
this.byteOffset += 4;
return value;
}
readInt8() {
const value = this.dataView.getInt8(this.byteOffset);
this.byteOffset += 1;
return value;
}
readInt16() {
const value = this.dataView.getInt16(this.byteOffset, this.littleEndian);
this.byteOffset += 2;
return value;
}
readInt32() {
const value = this.dataView.getInt32(this.byteOffset, this.littleEndian);
this.byteOffset += 4;
return value;
}
readFloat() {
const value = this.dataView.getFloat32(this.byteOffset, this.littleEndian);
this.byteOffset += 4;
return value;
}
readDouble() {
const value = this.dataView.getFloat64(this.byteOffset, this.littleEndian);
this.byteOffset += 8;
return value;
}
readVarInt() {
let result = 0;
let bytesRead = 0;
let nextByte;
do {
nextByte = this.dataView.getUint8(this.byteOffset + bytesRead);
result += (nextByte & 127) << 7 * bytesRead;
bytesRead++;
} while (nextByte >= 128);
this.byteOffset += bytesRead;
return result;
}
};
// src/lib/parse-twkb.ts
function isTWKB(arrayBuffer) {
const binaryReader = new BinaryReader(arrayBuffer);
const type = binaryReader.readUInt8();
const geometryType = type & 15;
if (geometryType < 1 || geometryType > 7) {
return false;
}
return true;
}
function parseTWKBGeometry(arrayBuffer) {
const binaryReader = new BinaryReader(arrayBuffer);
const context = parseTWKBHeader(binaryReader);
if (context.hasSizeAttribute) {
binaryReader.readVarInt();
}
if (context.hasBoundingBox) {
let dimensions = 2;
if (context.hasZ) {
dimensions++;
}
if (context.hasM) {
dimensions++;
}
for (let i = 0; i < dimensions; i++) {
binaryReader.readVarInt();
binaryReader.readVarInt();
}
}
return parseGeometry2(binaryReader, context, context.geometryType);
}
function parseTWKBHeader(binaryReader) {
const type = binaryReader.readUInt8();
const metadataHeader = binaryReader.readUInt8();
const geometryType = type & 15;
const precision = zigZagDecode(type >> 4);
const hasExtendedPrecision = Boolean(metadataHeader >> 3 & 1);
let hasZ = false;
let hasM = false;
let zPrecision = 0;
let zPrecisionFactor = 1;
let mPrecision = 0;
let mPrecisionFactor = 1;
if (hasExtendedPrecision) {
const extendedPrecision = binaryReader.readUInt8();
hasZ = (extendedPrecision & 1) === 1;
hasM = (extendedPrecision & 2) === 2;
zPrecision = zigZagDecode((extendedPrecision & 28) >> 2);
zPrecisionFactor = Math.pow(10, zPrecision);
mPrecision = zigZagDecode((extendedPrecision & 224) >> 5);
mPrecisionFactor = Math.pow(10, mPrecision);
}
return {
geometryType,
precision,
precisionFactor: Math.pow(10, precision),
hasBoundingBox: Boolean(metadataHeader >> 0 & 1),