UNPKG

@loaders.gl/wkt

Version:

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

4 lines 116 kB
{ "version": 3, "sources": ["index.js", "lib/utils/version.js", "lib/parse-wkt-crs.js", "wkt-crs-loader.js", "lib/encode-wkt-crs.js", "wkt-crs-writer.js", "lib/parse-wkt.js", "wkt-loader.js", "lib/encode-wkt.js", "wkt-writer.js", "lib/parse-wkb.js", "lib/parse-wkb-header.js", "wkb-loader.js", "lib/utils/binary-writer.js", "lib/encode-wkb.js", "wkb-writer.js", "lib/utils/hex-transcoder.js", "hex-wkb-loader.js", "lib/utils/binary-reader.js", "lib/parse-twkb.js", "twkb-loader.js", "lib/encode-twkb.js", "twkb-writer.js"], "sourcesContent": ["// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nexport { WKTCRSLoader } from \"./wkt-crs-loader.js\";\nexport { WKTCRSWriter } from \"./wkt-crs-writer.js\";\nexport { WKTLoader, WKTWorkerLoader } from \"./wkt-loader.js\";\nexport { WKTWriter } from \"./wkt-writer.js\";\nexport { WKBLoader, WKBWorkerLoader } from \"./wkb-loader.js\";\nexport { WKBWriter } from \"./wkb-writer.js\";\nexport { HexWKBLoader } from \"./hex-wkb-loader.js\";\nexport { TWKBLoader } from \"./twkb-loader.js\";\nexport { TWKBWriter } from \"./twkb-writer.js\";\n// EXPERIMENTAL APIs\nexport { isWKT } from \"./lib/parse-wkt.js\";\nexport { isWKB, parseWKBHeader } from \"./lib/parse-wkb-header.js\";\nexport { isTWKB } from \"./lib/parse-twkb.js\";\nexport { encodeHex, decodeHex } from \"./lib/utils/hex-transcoder.js\";\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Version constant cannot be imported, it needs to correspond to the build version of **this** module.\n// __VERSION__ is injected by babel-plugin-version-inline\n// @ts-ignore TS2304: Cannot find name '__VERSION__'.\nexport const VERSION = typeof \"4.3.3\" !== 'undefined' ? \"4.3.3\" : 'latest';\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license.\n/**\n *\n * @param wkt\n * @param options\n * @returns\n */\nexport function parseWKTCRS(wkt, options) {\n if (options?.debug) {\n console.log('[wktcrs] parse starting with\\n', wkt);\n }\n // move all keywords into first array item slot\n // from PARAM[12345, 67890] to [\"PARAM\", 12345, 67890]\n wkt = wkt.replace(/[A-Z][A-Z\\d_]+\\[/gi, (match) => `[\"${match.substr(0, match.length - 1)}\",`);\n // wrap variables in strings\n // from [...,NORTH] to [...,\"NORTH\"]\n wkt = wkt.replace(/, ?([A-Z][A-Z\\d_]+[,\\]])/gi, (match, p1) => {\n const varname = p1.substr(0, p1.length - 1);\n return ',' + `\"${options?.raw ? 'raw:' : ''}${varname}\"${p1[p1.length - 1]}`;\n });\n if (options?.raw) {\n // replace all numbers with strings\n wkt = wkt.replace(/, {0,2}(-?[\\.\\d]+)(?=,|\\])/g, function (match, p1) {\n return ',' + `\"${options?.raw ? 'raw:' : ''}${p1}\"`;\n });\n }\n // str should now be valid JSON\n if (options?.debug) {\n console.log(`[wktcrs] json'd wkt: '${wkt}'`);\n }\n let data;\n try {\n data = JSON.parse(wkt);\n }\n catch (error) {\n console.error(`[wktcrs] failed to parse '${wkt}'`);\n throw error;\n }\n if (options?.debug) {\n console.log(`[wktcrs] json parsed: '${wkt}'`);\n }\n function process(data, parent) {\n const kw = data[0];\n // after removing the first element with .shift()\n // data is now just an array of attributes\n data.forEach(function (it) {\n if (Array.isArray(it)) {\n process(it, data);\n }\n });\n const kwarr = `MULTIPLE_${kw}`;\n if (kwarr in parent) {\n parent[kwarr].push(data);\n }\n else if (kw in parent) {\n parent[kwarr] = [parent[kw], data];\n delete parent[kw];\n }\n else {\n parent[kw] = data;\n }\n return parent;\n }\n const result = process(data, [data]);\n if (options?.debug) {\n console.log('[wktcrs] parse returning', result);\n }\n if (options?.sort) {\n sort(result, options);\n }\n return result;\n}\nfunction sort(data, options) {\n const keys = Object.keys(data).filter((k) => !/\\d+/.test(k));\n const keywords = options?.keywords || [];\n if (!options?.keywords) {\n // try to find multiples\n const counts = {};\n if (Array.isArray(data)) {\n data.forEach((it) => {\n if (Array.isArray(it) && it.length >= 2 && typeof it[1] === 'string') {\n const k = it[0];\n if (!counts[k])\n counts[k] = 0;\n counts[k]++;\n }\n });\n for (const k in counts) {\n if (counts[k] > 0)\n keywords.push(k);\n }\n }\n }\n keys.forEach((key) => {\n data[key] = sort(data[key]);\n });\n keywords.forEach((key) => {\n const indices = [];\n const params = [];\n data.forEach((item, i) => {\n if (Array.isArray(item) && item[0] === key) {\n indices.push(i);\n params.push(item);\n }\n });\n params.sort((a, b) => {\n a = a[1].toString();\n b = b[1].toString();\n return a < b ? -1 : a > b ? 1 : 0;\n });\n // replace in order\n params.forEach((param, i) => {\n data[indices[i]] = param;\n });\n });\n return data;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { parseWKTCRS } from \"./lib/parse-wkt-crs.js\";\n/**\n * Well-Known text CRS loader\n * @see OGC Standard: https://www.ogc.org/standards/wkt-crs\n * @see Wikipedia Page: https://en.wikipedia.org/wiki/Well-known_text_representation_of_coordinate_reference_systems\n */\nexport const WKTCRSLoader = {\n dataType: null,\n batchType: null,\n name: 'WKT CRS (Well-Known Text Coordinate Reference System)',\n id: 'wkt-crs',\n module: 'wkt-crs',\n version: VERSION,\n worker: true,\n extensions: [],\n mimeTypes: ['text/plain'],\n category: 'json',\n text: true,\n options: {\n 'wkt-crs': {}\n },\n parse: async (arrayBuffer, options) => parseWKTCRS(new TextDecoder().decode(arrayBuffer), options?.['wkt-crs']),\n parseTextSync: (string, options) => parseWKTCRS(string, options?.['wkt-crs'])\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// parse-wkt-crs was forked from https://github.com/DanielJDufour/wkt-crs under Creative Commons CC0 1.0 license.\n/**\n * convert JSON representation of Well-Known Text\n * back to standard Well-Known Text\n */\nexport function encodeWKTCRS(wkt, options) {\n if (Array.isArray(wkt) && wkt.length === 1 && Array.isArray(wkt[0])) {\n wkt = wkt[0]; // ignore first extra wrapper array\n }\n const [kw, ...attrs] = wkt;\n const str = `${kw}[${attrs\n .map((attr) => {\n if (Array.isArray(attr)) {\n return encodeWKTCRS(attr, options);\n }\n else if (typeof attr === 'number') {\n return attr.toString();\n }\n else if (typeof attr === 'string') {\n // can't automatically convert all caps to varibale\n // because EPSG is string in AUTHORITY[\"EPSG\", ...]\n if (attr.startsWith('raw:')) {\n // convert \"raw:NORTH\" to NORTH\n return attr.replace('raw:', '');\n }\n return `\"${attr}\"`;\n }\n throw new Error(`[wktcrs] unexpected attribute \"${attr}\"`);\n })\n .join(',')}]`;\n return str;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { encodeWKTCRS } from \"./lib/encode-wkt-crs.js\";\n/**\n * Well-Known text CRS loader\n * @see OGC Standard: https://www.ogc.org/standards/wkt-crs\n * @see Wikipedia Page: https://en.wikipedia.org/wiki/Well-known_text_representation_of_coordinate_reference_systems\n */\nexport const WKTCRSWriter = {\n name: 'WKT CRS (Well-Known Text Coordinate Reference System)',\n id: 'wkt-crs',\n module: 'wkt-crs',\n version: VERSION,\n worker: true,\n extensions: [],\n mimeTypes: ['text/plain'],\n // category: 'json',\n text: true,\n options: {\n 'wkt-crs': {}\n },\n encode: async (wktcrs, options) => new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.['wkt-crs'])),\n encodeSync: (wktcrs, options) => new TextEncoder().encode(encodeWKTCRS(wktcrs, options?.['wkt-crs'])),\n encodeTextSync: (wktcrs, options) => encodeWKTCRS(wktcrs, options?.['wkt-crs'])\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent)\n/* eslint-disable */\n// @ts-nocheck\nconst numberRegexp = /[-+]?([0-9]*\\.[0-9]+|[0-9]+)([eE][-+]?[0-9]+)?/;\n// Matches sequences like '100 100' or '100 100 100'.\nconst tuples = new RegExp('^' + numberRegexp.source + '(\\\\s' + numberRegexp.source + '){1,}');\nexport const WKT_MAGIC_STRINGS = [\n 'POINT(',\n 'LINESTRING(',\n 'POLYGON(',\n 'MULTIPOINT(',\n 'MULTILINESTRING(',\n 'MULTIPOLYGON(',\n 'GEOMETRYCOLLECTION('\n // We only support this \"geojson\" subset of the OGC simple features standard\n];\n/**\n * Check if a string is WKT.\n * @param input A potential WKT geometry string\n * @return `true` if input appears to be a WKT geometry string, `false` otherwise\n\n * @note We only support the \"geojson\" subset of the OGC simple features standard\n * @todo Does not handle leading spaces which appear to be permitted per the spec:\n * \"A WKT string contains no white space outside of double quotes.\n * However padding with white space to improve human readability is permitted;\n * the examples of WKT that are included in this document have\n * spaces and line feeds inserted to improve clarity. Any padding is stripped out or ignored by parsers.\"\n */\nexport function isWKT(input) {\n return WKT_MAGIC_STRINGS.some((magicString) => input.startsWith(magicString));\n}\n/**\n * Parse WKT and return GeoJSON.\n * @param input A WKT geometry string\n * @return A GeoJSON geometry object\n *\n * @note We only support the \"geojson\" subset of the OGC simple features standard\n **/\nexport function parseWKT(input, options) {\n // TODO handle options.wkt.shape\n return parseWKTToGeometry(input, options);\n}\n/** Parse into GeoJSON geometry */\nfunction parseWKTToGeometry(input, options) {\n const parts = input.split(';');\n let _ = parts.pop();\n const srid = (parts.shift() || '').split('=').pop();\n const state = { parts, _, i: 0 };\n const geometry = parseGeometry(state);\n return options?.wkt?.crs ? addCRS(geometry, srid) : geometry;\n}\nfunction parseGeometry(state) {\n return (parsePoint(state) ||\n parseLineString(state) ||\n parsePolygon(state) ||\n parseMultiPoint(state) ||\n parseMultiLineString(state) ||\n parseMultiPolygon(state) ||\n parseGeometryCollection(state));\n}\n/** Adds a coordinate reference system as an undocumented */\nfunction addCRS(obj, srid) {\n if (obj && srid?.match(/\\d+/)) {\n const crs = {\n type: 'name',\n properties: {\n name: 'urn:ogc:def:crs:EPSG::' + srid\n }\n };\n // @ts-expect-error we assign an undocumented property on the geometry\n obj.crs = crs;\n }\n return obj;\n}\n// GEOMETRIES\nfunction parsePoint(state) {\n if (!$(/^(POINT(\\sz)?)/i, state)) {\n return null;\n }\n white(state);\n if (!$(/^(\\()/, state)) {\n return null;\n }\n const c = coords(state);\n if (!c) {\n return null;\n }\n white(state);\n if (!$(/^(\\))/, state)) {\n return null;\n }\n return {\n type: 'Point',\n coordinates: c[0]\n };\n}\nfunction parseMultiPoint(state) {\n if (!$(/^(MULTIPOINT)/i, state)) {\n return null;\n }\n white(state);\n const newCoordsFormat = state._?.substring(state._?.indexOf('(') + 1, state._.length - 1)\n .replace(/\\(/g, '')\n .replace(/\\)/g, '');\n state._ = 'MULTIPOINT (' + newCoordsFormat + ')';\n const c = multicoords(state);\n if (!c) {\n return null;\n }\n white(state);\n return {\n type: 'MultiPoint',\n coordinates: c\n };\n}\nfunction parseLineString(state) {\n if (!$(/^(LINESTRING(\\sz)?)/i, state)) {\n return null;\n }\n white(state);\n if (!$(/^(\\()/, state)) {\n return null;\n }\n const c = coords(state);\n if (!c) {\n return null;\n }\n if (!$(/^(\\))/, state)) {\n return null;\n }\n return {\n type: 'LineString',\n coordinates: c\n };\n}\nfunction parseMultiLineString(state) {\n if (!$(/^(MULTILINESTRING)/i, state))\n return null;\n white(state);\n const c = multicoords(state);\n if (!c) {\n return null;\n }\n white(state);\n return {\n // @ts-ignore\n type: 'MultiLineString',\n // @ts-expect-error\n coordinates: c\n };\n}\nfunction parsePolygon(state) {\n if (!$(/^(POLYGON(\\sz)?)/i, state)) {\n return null;\n }\n white(state);\n const c = multicoords(state);\n if (!c) {\n return null;\n }\n return {\n // @ts-ignore\n type: 'Polygon',\n // @ts-expect-error\n coordinates: c\n };\n}\nfunction parseMultiPolygon(state) {\n if (!$(/^(MULTIPOLYGON)/i, state)) {\n return null;\n }\n white(state);\n const c = multicoords(state);\n if (!c) {\n return null;\n }\n return {\n type: 'MultiPolygon',\n // @ts-expect-error\n coordinates: c\n };\n}\nfunction parseGeometryCollection(state) {\n const geometries = [];\n let geometry;\n if (!$(/^(GEOMETRYCOLLECTION)/i, state)) {\n return null;\n }\n white(state);\n if (!$(/^(\\()/, state)) {\n return null;\n }\n while ((geometry = parseGeometry(state))) {\n geometries.push(geometry);\n white(state);\n $(/^(,)/, state);\n white(state);\n }\n if (!$(/^(\\))/, state)) {\n return null;\n }\n return {\n type: 'GeometryCollection',\n geometries: geometries\n };\n}\n// COORDINATES\nfunction multicoords(state) {\n white(state);\n let depth = 0;\n const rings = [];\n const stack = [rings];\n let pointer = rings;\n let elem;\n while ((elem = $(/^(\\()/, state) || $(/^(\\))/, state) || $(/^(,)/, state) || $(tuples, state))) {\n if (elem === '(') {\n stack.push(pointer);\n pointer = [];\n stack[stack.length - 1].push(pointer);\n depth++;\n }\n else if (elem === ')') {\n // For the case: Polygon(), ...\n if (pointer.length === 0)\n return null;\n // @ts-ignore\n pointer = stack.pop();\n // the stack was empty, input was malformed\n if (!pointer)\n return null;\n depth--;\n if (depth === 0)\n break;\n }\n else if (elem === ',') {\n pointer = [];\n stack[stack.length - 1].push(pointer);\n }\n else if (!elem.split(/\\s/g).some(isNaN)) {\n Array.prototype.push.apply(pointer, elem.split(/\\s/g).map(parseFloat));\n }\n else {\n return null;\n }\n white(state);\n }\n if (depth !== 0)\n return null;\n return rings;\n}\nfunction coords(state) {\n const list = [];\n let item;\n let pt;\n while ((pt = $(tuples, state) || $(/^(,)/, state))) {\n if (pt === ',') {\n list.push(item);\n item = [];\n }\n else if (!pt.split(/\\s/g).some(isNaN)) {\n if (!item)\n item = [];\n Array.prototype.push.apply(item, pt.split(/\\s/g).map(parseFloat));\n }\n white(state);\n }\n if (item)\n list.push(item);\n else\n return null;\n return list.length ? list : null;\n}\n// HELPERS\nfunction $(regexp, state) {\n const match = state._?.substring(state.i).match(regexp);\n if (!match)\n return null;\n else {\n state.i += match[0].length;\n return match[0];\n }\n}\nfunction white(state) {\n $(/^\\s*/, state);\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { parseWKT, isWKT, WKT_MAGIC_STRINGS } from \"./lib/parse-wkt.js\";\n/**\n * Well-Known text worker loader\n */\nexport const WKTWorkerLoader = {\n dataType: null,\n batchType: null,\n name: 'WKT (Well-Known Text)',\n id: 'wkt',\n module: 'wkt',\n version: VERSION,\n worker: true,\n extensions: ['wkt'],\n mimeTypes: ['text/plain'],\n category: 'geometry',\n text: true,\n tests: WKT_MAGIC_STRINGS,\n testText: isWKT,\n options: {\n wkt: {\n shape: 'geojson-geometry',\n crs: true\n }\n }\n};\n/**\n * Well-Known text loader\n */\nexport const WKTLoader = {\n ...WKTWorkerLoader,\n parse: async (arrayBuffer, options) => parseWKT(new TextDecoder().decode(arrayBuffer), options),\n parseTextSync: (string, options) => parseWKT(string, options)\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Fork of https://github.com/mapbox/wellknown under ISC license (MIT/BSD-2-clause equivalent)\n/**\n * Stringifies a GeoJSON object into WKT\n * @param geojson\n * @returns string\n */\nexport function encodeWKT(geometry) {\n if (geometry.type === 'Feature') {\n geometry = geometry.geometry;\n }\n switch (geometry.type) {\n case 'Point':\n return `POINT ${wrapParens(pairWKT(geometry.coordinates))}`;\n case 'LineString':\n return `LINESTRING ${wrapParens(ringWKT(geometry.coordinates))}`;\n case 'Polygon':\n return `POLYGON ${wrapParens(ringsWKT(geometry.coordinates))}`;\n case 'MultiPoint':\n return `MULTIPOINT ${wrapParens(ringWKT(geometry.coordinates))}`;\n case 'MultiPolygon':\n return `MULTIPOLYGON ${wrapParens(multiRingsWKT(geometry.coordinates))}`;\n case 'MultiLineString':\n return `MULTILINESTRING ${wrapParens(ringsWKT(geometry.coordinates))}`;\n case 'GeometryCollection':\n return `GEOMETRYCOLLECTION ${wrapParens(geometry.geometries.map(encodeWKT).join(', '))}`;\n default:\n throw new Error('stringify requires a valid GeoJSON Feature or geometry object as input');\n }\n}\nfunction pairWKT(c) {\n return c.join(' ');\n}\nfunction ringWKT(r) {\n return r.map(pairWKT).join(', ');\n}\nfunction ringsWKT(r) {\n return r.map(ringWKT).map(wrapParens).join(', ');\n}\nfunction multiRingsWKT(r) {\n return r.map(ringsWKT).map(wrapParens).join(', ');\n}\nfunction wrapParens(s) {\n return `(${s})`;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { encodeWKT } from \"./lib/encode-wkt.js\";\n/**\n * WKT exporter\n */\nexport const WKTWriter = {\n name: 'WKT (Well Known Text)',\n id: 'wkt',\n module: 'wkt',\n version: VERSION,\n extensions: ['wkt'],\n text: true,\n encode: async (geometry) => encodeWKTSync(geometry),\n encodeSync: encodeWKTSync,\n encodeTextSync: encodeWKT,\n options: {\n wkt: {}\n }\n};\nfunction encodeWKTSync(geometry) {\n return new TextEncoder().encode(encodeWKT(geometry)).buffer;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { binaryToGeometry } from '@loaders.gl/gis';\nimport { parseWKBHeader, WKBGeometryType } from \"./parse-wkb-header.js\";\nexport function parseWKB(arrayBuffer, options) {\n const binaryGeometry = parseWKBToBinary(arrayBuffer, options);\n const shape = options?.wkb?.shape || 'binary-geometry';\n switch (shape) {\n case 'binary-geometry':\n return binaryGeometry;\n case 'geojson-geometry':\n return binaryToGeometry(binaryGeometry);\n case 'geometry':\n // eslint-disable-next-line no-console\n console.error('WKBLoader: \"geometry\" shape is deprecated, use \"binary-geometry\" instead');\n return binaryToGeometry(binaryGeometry);\n default:\n throw new Error(shape);\n }\n}\nexport function parseWKBToBinary(arrayBuffer, options) {\n const dataView = new DataView(arrayBuffer);\n const wkbHeader = parseWKBHeader(dataView);\n const { geometryType, dimensions, littleEndian } = wkbHeader;\n const offset = wkbHeader.byteOffset;\n switch (geometryType) {\n case WKBGeometryType.Point:\n const point = parsePoint(dataView, offset, dimensions, littleEndian);\n return point.geometry;\n case WKBGeometryType.LineString:\n const line = parseLineString(dataView, offset, dimensions, littleEndian);\n return line.geometry;\n case WKBGeometryType.Polygon:\n const polygon = parsePolygon(dataView, offset, dimensions, littleEndian);\n return polygon.geometry;\n case WKBGeometryType.MultiPoint:\n const multiPoint = parseMultiPoint(dataView, offset, dimensions, littleEndian);\n multiPoint.type = 'Point';\n return multiPoint;\n case WKBGeometryType.MultiLineString:\n const multiLine = parseMultiLineString(dataView, offset, dimensions, littleEndian);\n multiLine.type = 'LineString';\n return multiLine;\n case WKBGeometryType.MultiPolygon:\n const multiPolygon = parseMultiPolygon(dataView, offset, dimensions, littleEndian);\n multiPolygon.type = 'Polygon';\n return multiPolygon;\n // case WKBGeometryType.GeometryCollection:\n // TODO: handle GeometryCollections\n // return parseGeometryCollection(dataView, offset, dimensions, littleEndian);\n default:\n throw new Error(`WKB: Unsupported geometry type: ${geometryType}`);\n }\n}\n// Primitives; parse point and linear ring\nfunction parsePoint(dataView, offset, dimension, littleEndian) {\n const positions = new Float64Array(dimension);\n for (let i = 0; i < dimension; i++) {\n positions[i] = dataView.getFloat64(offset, littleEndian);\n offset += 8;\n }\n return {\n geometry: { type: 'Point', positions: { value: positions, size: dimension } },\n offset\n };\n}\nfunction parseLineString(dataView, offset, dimension, littleEndian) {\n const nPoints = dataView.getUint32(offset, littleEndian);\n offset += 4;\n // Instantiate array\n const positions = new Float64Array(nPoints * dimension);\n for (let i = 0; i < nPoints * dimension; i++) {\n positions[i] = dataView.getFloat64(offset, littleEndian);\n offset += 8;\n }\n const pathIndices = [0];\n if (nPoints > 0) {\n pathIndices.push(nPoints);\n }\n return {\n geometry: {\n type: 'LineString',\n positions: { value: positions, size: dimension },\n pathIndices: { value: new Uint32Array(pathIndices), size: 1 }\n },\n offset\n };\n}\n// https://stackoverflow.com/a/55261098\nconst cumulativeSum = (sum) => (value) => (sum += value);\nfunction parsePolygon(dataView, offset, dimension, littleEndian) {\n const nRings = dataView.getUint32(offset, littleEndian);\n offset += 4;\n const rings = [];\n for (let i = 0; i < nRings; i++) {\n const parsed = parseLineString(dataView, offset, dimension, littleEndian);\n const { positions } = parsed.geometry;\n offset = parsed.offset;\n rings.push(positions.value);\n }\n const concatenatedPositions = new Float64Array(concatTypedArrays(rings).buffer);\n const polygonIndices = [0];\n if (concatenatedPositions.length > 0) {\n polygonIndices.push(concatenatedPositions.length / dimension);\n }\n const primitivePolygonIndices = rings.map((l) => l.length / dimension).map(cumulativeSum(0));\n primitivePolygonIndices.unshift(0);\n return {\n geometry: {\n type: 'Polygon',\n positions: { value: concatenatedPositions, size: dimension },\n polygonIndices: {\n value: new Uint32Array(polygonIndices),\n size: 1\n },\n primitivePolygonIndices: { value: new Uint32Array(primitivePolygonIndices), size: 1 }\n },\n offset\n };\n}\nfunction parseMultiPoint(dataView, offset, dimension, littleEndian) {\n const nPoints = dataView.getUint32(offset, littleEndian);\n offset += 4;\n const binaryPointGeometries = [];\n for (let i = 0; i < nPoints; i++) {\n // Byte order for point\n const littleEndianPoint = dataView.getUint8(offset) === 1;\n offset++;\n // Assert point type\n if (dataView.getUint32(offset, littleEndianPoint) % 1000 !== 1) {\n throw new Error('WKB: Inner geometries of MultiPoint not of type Point');\n }\n offset += 4;\n const parsed = parsePoint(dataView, offset, dimension, littleEndianPoint);\n offset = parsed.offset;\n binaryPointGeometries.push(parsed.geometry);\n }\n return concatenateBinaryPointGeometries(binaryPointGeometries, dimension);\n}\nfunction parseMultiLineString(dataView, offset, dimension, littleEndian) {\n const nLines = dataView.getUint32(offset, littleEndian);\n offset += 4;\n const binaryLineGeometries = [];\n for (let i = 0; i < nLines; i++) {\n // Byte order for line\n const littleEndianLine = dataView.getUint8(offset) === 1;\n offset++;\n // Assert type LineString\n if (dataView.getUint32(offset, littleEndianLine) % 1000 !== 2) {\n throw new Error('WKB: Inner geometries of MultiLineString not of type LineString');\n }\n offset += 4;\n const parsed = parseLineString(dataView, offset, dimension, littleEndianLine);\n offset = parsed.offset;\n binaryLineGeometries.push(parsed.geometry);\n }\n return concatenateBinaryLineGeometries(binaryLineGeometries, dimension);\n}\nfunction parseMultiPolygon(dataView, offset, dimension, littleEndian) {\n const nPolygons = dataView.getUint32(offset, littleEndian);\n offset += 4;\n const binaryPolygonGeometries = [];\n for (let i = 0; i < nPolygons; i++) {\n // Byte order for polygon\n const littleEndianPolygon = dataView.getUint8(offset) === 1;\n offset++;\n // Assert type Polygon\n if (dataView.getUint32(offset, littleEndianPolygon) % 1000 !== 3) {\n throw new Error('WKB: Inner geometries of MultiPolygon not of type Polygon');\n }\n offset += 4;\n const parsed = parsePolygon(dataView, offset, dimension, littleEndianPolygon);\n offset = parsed.offset;\n binaryPolygonGeometries.push(parsed.geometry);\n }\n return concatenateBinaryPolygonGeometries(binaryPolygonGeometries, dimension);\n}\n// TODO - move to loaders.gl/schema/gis\nfunction concatenateBinaryPointGeometries(binaryPointGeometries, dimension) {\n const positions = binaryPointGeometries.map((geometry) => geometry.positions.value);\n const concatenatedPositions = new Float64Array(concatTypedArrays(positions).buffer);\n return {\n type: 'Point',\n positions: { value: concatenatedPositions, size: dimension }\n };\n}\nfunction concatenateBinaryLineGeometries(binaryLineGeometries, dimension) {\n const lines = binaryLineGeometries.map((geometry) => geometry.positions.value);\n const concatenatedPositions = new Float64Array(concatTypedArrays(lines).buffer);\n const pathIndices = lines.map((line) => line.length / dimension).map(cumulativeSum(0));\n pathIndices.unshift(0);\n return {\n type: 'LineString',\n positions: { value: concatenatedPositions, size: dimension },\n pathIndices: { value: new Uint32Array(pathIndices), size: 1 }\n };\n}\nfunction concatenateBinaryPolygonGeometries(binaryPolygonGeometries, dimension) {\n const polygons = [];\n const primitivePolygons = [];\n for (const binaryPolygon of binaryPolygonGeometries) {\n const { positions, primitivePolygonIndices } = binaryPolygon;\n polygons.push(positions.value);\n primitivePolygons.push(primitivePolygonIndices.value);\n }\n const concatenatedPositions = new Float64Array(concatTypedArrays(polygons).buffer);\n const polygonIndices = polygons.map((p) => p.length / dimension).map(cumulativeSum(0));\n polygonIndices.unshift(0);\n // Combine primitivePolygonIndices from each individual polygon\n const primitivePolygonIndices = [0];\n for (const primitivePolygon of primitivePolygons) {\n primitivePolygonIndices.push(...primitivePolygon\n .filter((x) => x > 0)\n .map((x) => x + primitivePolygonIndices[primitivePolygonIndices.length - 1]));\n }\n return {\n type: 'Polygon',\n positions: { value: concatenatedPositions, size: dimension },\n polygonIndices: { value: new Uint32Array(polygonIndices), size: 1 },\n primitivePolygonIndices: { value: new Uint32Array(primitivePolygonIndices), size: 1 }\n };\n}\n// TODO: remove copy; import from typed-array-utils\n// modules/math/src/geometry/typed-arrays/typed-array-utils.js\nfunction concatTypedArrays(arrays) {\n let byteLength = 0;\n for (let i = 0; i < arrays.length; ++i) {\n byteLength += arrays[i].byteLength;\n }\n const buffer = new Uint8Array(byteLength);\n let byteOffset = 0;\n for (let i = 0; i < arrays.length; ++i) {\n const data = new Uint8Array(arrays[i].buffer);\n byteLength = data.length;\n for (let j = 0; j < byteLength; ++j) {\n buffer[byteOffset++] = data[j];\n }\n }\n return buffer;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nconst EWKB_FLAG_Z = 0x80000000;\nconst EWKB_FLAG_M = 0x40000000;\nconst EWKB_FLAG_SRID = 0x20000000;\nconst MAX_SRID = 10000; // TBD: Assume no more than 10K SRIDs are defined\n/**\n * Integer code for geometry types in WKB and related formats\n * Reference: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary\n */\nexport var WKBGeometryType;\n(function (WKBGeometryType) {\n WKBGeometryType[WKBGeometryType[\"Point\"] = 1] = \"Point\";\n WKBGeometryType[WKBGeometryType[\"LineString\"] = 2] = \"LineString\";\n WKBGeometryType[WKBGeometryType[\"Polygon\"] = 3] = \"Polygon\";\n WKBGeometryType[WKBGeometryType[\"MultiPoint\"] = 4] = \"MultiPoint\";\n WKBGeometryType[WKBGeometryType[\"MultiLineString\"] = 5] = \"MultiLineString\";\n WKBGeometryType[WKBGeometryType[\"MultiPolygon\"] = 6] = \"MultiPolygon\";\n WKBGeometryType[WKBGeometryType[\"GeometryCollection\"] = 7] = \"GeometryCollection\";\n})(WKBGeometryType || (WKBGeometryType = {}));\n/** Sanity checks that first to 5-9 bytes could represent a supported WKB dialect header */\nexport function isWKB(arrayBuffer) {\n const dataView = new DataView(arrayBuffer);\n let byteOffset = 0;\n const endianness = dataView.getUint8(byteOffset);\n byteOffset += 1;\n // Check valid endianness (only 0 or 1 are allowed)\n if (endianness > 1) {\n return false;\n }\n const littleEndian = endianness === 1;\n const geometry = dataView.getUint32(byteOffset, littleEndian);\n byteOffset += 4;\n // check valid geometry type (we don't support extension geometries)\n const geometryType = geometry & 0x07;\n if (geometryType === 0 || geometryType > 7) {\n return false;\n }\n const geometryFlags = geometry - geometryType;\n // Accept iso-wkb flags\n if (geometryFlags === 0 ||\n geometryFlags === 1000 ||\n geometryFlags === 2000 ||\n geometryFlags === 3000) {\n return true;\n }\n // Accept ewkb flags but reject otherwise\n if ((geometryFlags & ~(EWKB_FLAG_Z | EWKB_FLAG_M | EWKB_FLAG_SRID)) !== 0) {\n return false;\n }\n if (geometryFlags & EWKB_FLAG_SRID) {\n const srid = dataView.getUint32(byteOffset, littleEndian);\n byteOffset += 4;\n if (srid > MAX_SRID) {\n return false;\n }\n }\n return true;\n}\n/**\n * Parses header and provides a byteOffset to start of geometry data\n * @param dataView\n * @param target optionally supply a WKBHeader object to avoid creating a new object for every call\n * @returns a header object describing the WKB data\n */\n// eslint-disable-next-line max-statements\nexport function parseWKBHeader(dataView, target) {\n const wkbHeader = Object.assign(target || {}, {\n type: 'wkb',\n geometryType: 1,\n dimensions: 2,\n coordinates: 'xy',\n littleEndian: true,\n byteOffset: 0\n });\n // Check endianness of data\n wkbHeader.littleEndian = dataView.getUint8(wkbHeader.byteOffset) === 1;\n wkbHeader.byteOffset++;\n // 4-digit code representing dimension and type of geometry\n const geometryCode = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian);\n wkbHeader.byteOffset += 4;\n wkbHeader.geometryType = (geometryCode & 0x7);\n // Check if iso-wkb variant: iso-wkb adds 1000, 2000 or 3000 to the geometry code\n const isoType = (geometryCode - wkbHeader.geometryType) / 1000;\n switch (isoType) {\n case 0:\n break;\n case 1:\n wkbHeader.type = 'iso-wkb';\n wkbHeader.dimensions = 3;\n wkbHeader.coordinates = 'xyz';\n break;\n case 2:\n wkbHeader.type = 'iso-wkb';\n wkbHeader.dimensions = 3;\n wkbHeader.coordinates = 'xym';\n break;\n case 3:\n wkbHeader.type = 'iso-wkb';\n wkbHeader.dimensions = 4;\n wkbHeader.coordinates = 'xyzm';\n break;\n default:\n throw new Error(`WKB: Unsupported iso-wkb type: ${isoType}`);\n }\n // Check if EWKB variant. Uses bitmasks for Z&M dimensions as well as optional SRID field\n const ewkbZ = geometryCode & EWKB_FLAG_Z;\n const ewkbM = geometryCode & EWKB_FLAG_M;\n const ewkbSRID = geometryCode & EWKB_FLAG_SRID;\n if (ewkbZ && ewkbM) {\n wkbHeader.type = 'ewkb';\n wkbHeader.dimensions = 4;\n wkbHeader.coordinates = 'xyzm';\n }\n else if (ewkbZ) {\n wkbHeader.type = 'ewkb';\n wkbHeader.dimensions = 3;\n wkbHeader.coordinates = 'xyz';\n }\n else if (ewkbM) {\n wkbHeader.type = 'ewkb';\n wkbHeader.dimensions = 3;\n wkbHeader.coordinates = 'xym';\n }\n // If SRID present read four more bytes\n if (ewkbSRID) {\n wkbHeader.type = 'ewkb';\n // 4-digit code representing dimension and type of geometry\n wkbHeader.srid = dataView.getUint32(wkbHeader.byteOffset, wkbHeader.littleEndian);\n wkbHeader.byteOffset += 4;\n }\n return wkbHeader;\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\nimport { VERSION } from \"./lib/utils/version.js\";\nimport { parseWKB } from \"./lib/parse-wkb.js\";\nimport { isWKB } from \"./lib/parse-wkb-header.js\";\n/**\n * Worker loader for WKB (Well-Known Binary)\n */\nexport const WKBWorkerLoader = {\n dataType: null,\n batchType: null,\n name: 'WKB',\n id: 'wkb',\n module: 'wkt',\n version: VERSION,\n worker: true,\n category: 'geometry',\n extensions: ['wkb'],\n mimeTypes: [],\n // TODO can we define static, serializable tests, eg. some binary strings?\n tests: [isWKB],\n options: {\n wkb: {\n shape: 'binary-geometry' // 'geojson-geometry'\n }\n }\n};\n/**\n * Loader for WKB (Well-Known Binary)\n */\nexport const WKBLoader = {\n ...WKBWorkerLoader,\n parse: async (arrayBuffer) => parseWKB(arrayBuffer),\n parseSync: parseWKB\n};\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz\nconst LE = true;\nconst BE = false;\nexport class BinaryWriter {\n arrayBuffer;\n dataView;\n byteOffset = 0;\n allowResize = false;\n constructor(size, allowResize) {\n this.arrayBuffer = new ArrayBuffer(size);\n this.dataView = new DataView(this.arrayBuffer);\n this.byteOffset = 0;\n this.allowResize = allowResize || false;\n }\n writeUInt8(value) {\n this._ensureSize(1);\n this.dataView.setUint8(this.byteOffset, value);\n this.byteOffset += 1;\n }\n writeUInt16LE(value) {\n this._ensureSize(2);\n this.dataView.setUint16(this.byteOffset, value, LE);\n this.byteOffset += 2;\n }\n writeUInt16BE(value) {\n this._ensureSize(2);\n this.dataView.setUint16(this.byteOffset, value, BE);\n this.byteOffset += 2;\n }\n writeUInt32LE(value) {\n this._ensureSize(4);\n this.dataView.setUint32(this.byteOffset, value, LE);\n this.byteOffset += 4;\n }\n writeUInt32BE(value) {\n this._ensureSize(4);\n this.dataView.setUint32(this.byteOffset, value, BE);\n this.byteOffset += 4;\n }\n writeInt8(value) {\n this._ensureSize(1);\n this.dataView.setInt8(this.byteOffset, value);\n this.byteOffset += 1;\n }\n writeInt16LE(value) {\n this._ensureSize(2);\n this.dataView.setInt16(this.byteOffset, value, LE);\n this.byteOffset += 2;\n }\n writeInt16BE(value) {\n this._ensureSize(2);\n this.dataView.setInt16(this.byteOffset, value, BE);\n this.byteOffset += 2;\n }\n writeInt32LE(value) {\n this._ensureSize(4);\n this.dataView.setInt32(this.byteOffset, value, LE);\n this.byteOffset += 4;\n }\n writeInt32BE(value) {\n this._ensureSize(4);\n this.dataView.setInt32(this.byteOffset, value, BE);\n this.byteOffset += 4;\n }\n writeFloatLE(value) {\n this._ensureSize(4);\n this.dataView.setFloat32(this.byteOffset, value, LE);\n this.byteOffset += 4;\n }\n writeFloatBE(value) {\n this._ensureSize(4);\n this.dataView.setFloat32(this.byteOffset, value, BE);\n this.byteOffset += 4;\n }\n writeDoubleLE(value) {\n this._ensureSize(8);\n this.dataView.setFloat64(this.byteOffset, value, LE);\n this.byteOffset += 8;\n }\n writeDoubleBE(value) {\n this._ensureSize(8);\n this.dataView.setFloat64(this.byteOffset, value, BE);\n this.byteOffset += 8;\n }\n /** A varint uses a variable number of bytes */\n writeVarInt(value) {\n // TODO - ensure size?\n let length = 1;\n while ((value & 0xffffff80) !== 0) {\n this.writeUInt8((value & 0x7f) | 0x80);\n value >>>= 7;\n length++;\n }\n this.writeUInt8(value & 0x7f);\n return length;\n }\n /** Append another ArrayBuffer to this ArrayBuffer */\n writeBuffer(arrayBuffer) {\n this._ensureSize(arrayBuffer.byteLength);\n const tempArray = new Uint8Array(this.arrayBuffer);\n tempArray.set(new Uint8Array(arrayBuffer), this.byteOffset);\n this.byteOffset += arrayBuffer.byteLength;\n }\n /** Resizes this.arrayBuffer if not enough space */\n _ensureSize(size) {\n if (this.arrayBuffer.byteLength < this.byteOffset + size) {\n if (this.allowResize) {\n const newArrayBuffer = new ArrayBuffer(this.byteOffset + size);\n const tempArray = new Uint8Array(newArrayBuffer);\n tempArray.set(new Uint8Array(this.arrayBuffer));\n this.arrayBuffer = newArrayBuffer;\n }\n else {\n throw new Error('BinaryWriter overflow');\n }\n }\n }\n}\n", "// loaders.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n// Forked from https://github.com/cschwarz/wkx under MIT license, Copyright (c) 2013 Christian Schwarz\n// Reference: https://www.ogc.org/standards/sfa\nimport { BinaryWriter } from \"./utils/binary-writer.js\";\n/**\n * Integer code for geometry type\n * Reference: https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary\n */\nexport var WKB;\n(function (WKB) {\n WKB[WKB[\"Point\"] = 1] = \"Point\";\n WKB[WKB[\"LineString\"] = 2] = \"LineString\";\n WKB[WKB[\"Polygon\"] = 3] = \"Polygon\";\n WKB[WKB[\"MultiPoint\"] = 4] = \"MultiPoint\";\n WKB[WKB[\"MultiLineString\"] = 5] = \"MultiLineString\";\n WKB[WKB[\"MultiPolygon\"] = 6] = \"MultiPolygon\";\n WKB[WKB[\"GeometryCollection\"] = 7] = \"GeometryCollection\";\n})(WKB || (WKB = {}));\n/**\n * Encodes a GeoJSON object into WKB\n * @param geojson A GeoJSON Feature or Geometry\n * @returns string\n */\nexport function encodeWKB(geometry, options = {}) {\n if (geometry.type === 'Feature') {\n geometry = geometry.geometry;\n }\n switch (geometry.type) {\n case 'Point':\n return encodePoint(geometry.coordinates, options);\n case 'LineString':\n return encodeLineString(geometry.coordinates, options);\n case 'Polygon':\n return encodePolygon(geometry.coordinates, options);\n case 'MultiPoint':\n return encodeMultiPoint(geometry, options);\n case 'MultiPolygon':\n return encodeMultiPolygon(geometry, options);\n case 'MultiLineString':\n return encodeMultiLineString(geometry, options);\n case 'GeometryCollection':\n return encodeGeometryCollection(geometry, options);\n default:\n const exhaustiveCheck = geometry;\n throw new Error(`Unhandled case: ${exhaustiveCheck}`);\n }\n}\n/** Calculate the binary size (in the WKB encoding) of a specific GeoJSON geometry */\nfunction getGeometrySize(geometry, options) {\n switch (geometry.type) {\n case 'Point':\n return getPointSize(options);\n case 'LineString':\n return getLineStringSize(geometry.coordinates, options);\n case 'Polygon':\n return getPolygonSize(geometry.coordinates, options);\n case 'MultiPoint':\n return getMultiPointSize(geometry, options);\n case 'MultiPolygon':\n return getMultiPolygonSize(geometry, options);\n case 'MultiLineString':\n return getMultiLineStringSize(geometry, options);\n case 'GeometryCollection':\n return getGeometryCollectionSize(geometry, options);\n default:\n const exhaustiveCheck = geometry;\n throw new Error(`Unhandled case: ${exhaustiveCheck}`);\n }\n}\n/** Encode Point geometry as WKB ArrayBuffer */\nfunction encodePoint(coordinates, options) {\n const writer = new BinaryWriter(getPointSize(options));\n writer.writeInt8(1);\n writeWkbType(writer, WKB.Point, options);\n // I believe this special case is to handle writing Point(NaN, NaN) correctly\n if (typeof coordinates[0] === 'undefined' && typeof coordinates[1] === 'undefined') {\n writer.writeDoubleLE(NaN);\n writer.writeDoubleLE(NaN);\n if (options.hasZ) {\n writer.writeDoubleLE(NaN);\n }\n if (options.hasM) {\n writer.writeDoubleLE(NaN);\n }\n }\n else {\n writeCoordinate(writer, coordinates, options);\n }\n return writer.arrayBuffer;\n}\n/** Write coordinate to buffer */\nfunction writeCoordinate(writer, coordinate, options) {\n writer.writeDoubleLE(coordinate[0]);\n writer.writeDoubleLE(coordinate[1]);\n if (options.hasZ) {\n writer.writeDoubleLE(coordinate[2]);\n }\n if (options.hasM) {\n writer.writeDoubleLE(coordinate[3]);\n }\n}\n/** Get encoded size of Point geometry */\nfunction getPointSize(options) {\n const coordinateSize = getCoordinateSize(options);\n return 1 + 4 + coordinateSize;\n}\n/** Encode LineString geometry as WKB ArrayBuffer */\nfunction encodeLineString(coordinates, options) {\n const size = getLineStringSize(coordinates, options);\n const writer = new BinaryWriter(size);\n writer.writeInt8(1);\n writeWkbType(writer, WKB.LineString, options);\n writer.writeUInt32LE(coordinates.length);\n for (const coordinate of coordinates) {\n writeCoordinate(writer, coordinate, options);\n }\n return writer.arrayBuffer;\n}\n/** Get encoded size of LineString geometry */\nfunction getLineStringSize(coordinates, options) {\n const coordinateSize = getCoordinateSize(options);\n return 1 + 4 + 4 + coordinates.length * coordinateSize;\n}\n/** Encode Polygon geometry as WKB ArrayBuffer */\nfunction encodePolygon(coordinates, options) {\n const writer = new BinaryWriter(getPolygonSize(coordinates, options));\n writer.writeInt8(1);\n writeWkbType(writer, WKB.Polygon, options);\n const [exteriorRing, ...interiorRings] = coordinates;\n if (exteriorRing.length > 0) {\n writer.writeUInt32LE(1 + interiorRings.length);\n writer.writeUInt32LE(exteriorRing.length);\n }\n else {\n writer.writeUInt32LE(0);\n }\n for (const coordinate of exteriorRing) {\n writeCoordinate(writer, coordinate, options);\n }\n for (const interiorRing of interiorRings) {\n writer.writeUInt32LE(interiorRing.length);\n for (const coordinate of interiorRing) {\n writeCoordinate(writer, coordinate, options);\n }\n }\n return writer.arrayBuffer;\n}\n/** Get encoded size of Polygon geometry */\nfunction getPolygonSize(coordinates, options) {\n const coordinateSize = getCoordinateSize(options);\n const [exteriorRing, ...interiorRings] = coordinates;\n let size = 1 + 4 + 4;\n if (exteriorRing.length > 0) {\n size += 4 + exteriorRing.length * coordinateSize;\n }\n for (const interiorRing of interiorRings) {\n size += 4 + interiorRing.length * coordinateSize;\n }\n return size;\n}\n/** Encode MultiPoint geometry as WKB ArrayBufer */\nfunction encodeMultiPoint(multiPoint, options) {\n const writer = new BinaryWriter(getMultiPointSize(multiPoint, options));\n const points = multiPoint.coordinates;\n writer.writeInt8(1);\n writeWkbType(writer, WKB.MultiPoint, options);\n writer.writeUInt32LE(points.length);\n for (const point of points) {\n // TODO: add srid to this options object? {srid: multiPoint.srid}\n const arrayBuffer = encodePoint(point, options);\n writer.writeBuffer(arrayBuffer);\n }\n return writer.arrayBuffer;\n}\n/** Get encoded size of MultiPoint geometry */\nfunction getMultiPointSize(multiPoint, options) {\n let coordinateSize = getCoordinateSize(options);\n const points = multiPoint.coordinates;\n // This is because each point has a 5-byte header?\n coordinateSize += 5;\n return 1 + 4 + 4 + points.length * coordinateSize;\n}\n/** Encode MultiLineString geometry as WKB ArrayBufer */\nfunction encodeMultiLineString(multiLineString, options) {\n const writer = new BinaryWriter(getMultiLineStringSize(multiLineString, options));\n const lineStrings = multiLineString.coordinates;\n writer.writeInt8(1);\n writeWkbType(writer, WKB.MultiLineString, options);\n writer.writeUInt32LE(lineStrings.length);\n for (const lineString of lineStrings) {\n // TODO: Handle srid?\n const encodedLineString = encodeLineString(lineString, options);\n writer.writeBuffer(encodedLineString);\n }\n return writer.arrayBuffer;\n}\n/** Get encoded size of MultiLineString geometry */\nfunction getMultiLineStringSize(multiLineString, options) {\n let size = 1 + 4 + 4;\n const lineStrings = multiLineString.coordinates;\n for (const lineString of lineStrings) {\n size += getLineStringSize(lineString, options);\n }\n return size;\n}\nfunction encodeMultiPolygon(multiPolygon, options) {\n const writer = new BinaryWriter(getMultiPolygonSize(multiPolygon, options));\n const polygons = multiPolygon.coordinates;\n writer.writeInt8(1);\n writeWkbType(writer, WKB.MultiPolygon, options);\n writer.writeUInt32LE(polygons.length);\n for (const polygon of polygons) {\n const encodedPolygon = encodePolygon(polygon, options);\n writer.writeBuffer(encodedPolygon);\n }\n return writer.arrayBuffer;\n}\nfunction getMultiPolygonSize(multiPolygon, options) {\n let size = 1 + 4 + 4;\n const polygons = multiPolygon.coordinates;\n for (const polygon of polygons) {\n size += getPolygonSize(polygon, options);\n }\n return size;\n}\nfunction encodeGeometryCollection(collection, options) {\n const writer = new BinaryWriter(getGeometryCollectionSize(collection, options));\n writer.writeInt8(1);\n writeWkbType(w