File size: 3,408 Bytes
9ae1216 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /**
* WKB (Well-Known Binary) decoder for geometry objects.
*
* @param {DataReader} reader
* @returns {Geometry} geometry object
*/
export function wkbToGeojson(reader) {
const flags = getFlags(reader)
if (flags.type === 1) { // Point
return { type: 'Point', coordinates: readPosition(reader, flags) }
} else if (flags.type === 2) { // LineString
return { type: 'LineString', coordinates: readLine(reader, flags) }
} else if (flags.type === 3) { // Polygon
return { type: 'Polygon', coordinates: readPolygon(reader, flags) }
} else if (flags.type === 4) { // MultiPoint
const points = []
for (let i = 0; i < flags.count; i++) {
points.push(readPosition(reader, getFlags(reader)))
}
return { type: 'MultiPoint', coordinates: points }
} else if (flags.type === 5) { // MultiLineString
const lines = []
for (let i = 0; i < flags.count; i++) {
lines.push(readLine(reader, getFlags(reader)))
}
return { type: 'MultiLineString', coordinates: lines }
} else if (flags.type === 6) { // MultiPolygon
const polygons = []
for (let i = 0; i < flags.count; i++) {
polygons.push(readPolygon(reader, getFlags(reader)))
}
return { type: 'MultiPolygon', coordinates: polygons }
} else if (flags.type === 7) { // GeometryCollection
const geometries = []
for (let i = 0; i < flags.count; i++) {
geometries.push(wkbToGeojson(reader))
}
return { type: 'GeometryCollection', geometries }
} else {
throw new Error(`Unsupported geometry type: ${flags.type}`)
}
}
/**
* Extract ISO WKB flags and base geometry type.
*
* @param {DataReader} reader
* @returns {WkbFlags}
*/
function getFlags(reader) {
const { view } = reader
const littleEndian = view.getUint8(reader.offset++) === 1
const rawType = view.getUint32(reader.offset, littleEndian)
reader.offset += 4
const type = rawType % 1000
const flags = Math.floor(rawType / 1000)
let count = 0
if (type > 1 && type <= 7) {
count = view.getUint32(reader.offset, littleEndian)
reader.offset += 4
}
// XY, XYZ, XYM, XYZM
let dim = 2
if (flags) dim++
if (flags === 3) dim++
return { littleEndian, type, dim, count }
}
/**
* @param {DataReader} reader
* @param {WkbFlags} flags
* @returns {number[]}
*/
function readPosition(reader, flags) {
const points = []
for (let i = 0; i < flags.dim; i++) {
const coord = reader.view.getFloat64(reader.offset, flags.littleEndian)
reader.offset += 8
points.push(coord)
}
return points
}
/**
* @param {DataReader} reader
* @param {WkbFlags} flags
* @returns {number[][]}
*/
function readLine(reader, flags) {
const points = []
for (let i = 0; i < flags.count; i++) {
points.push(readPosition(reader, flags))
}
return points
}
/**
* @param {DataReader} reader
* @param {WkbFlags} flags
* @returns {number[][][]}
*/
function readPolygon(reader, flags) {
const { view } = reader
const rings = []
for (let r = 0; r < flags.count; r++) {
const count = view.getUint32(reader.offset, flags.littleEndian)
reader.offset += 4
rings.push(readLine(reader, { ...flags, count }))
}
return rings
}
/**
* @typedef {object} WkbFlags
* @property {boolean} littleEndian
* @property {number} type
* @property {number} dim
* @property {number} count
*/
/**
* @import {DataReader, Geometry} from '../src/types.js'
*/
|