diff --git a/examples/wkb.html b/examples/wkb.html
new file mode 100644
index 0000000000..faa782a5b0
--- /dev/null
+++ b/examples/wkb.html
@@ -0,0 +1,9 @@
+---
+layout: example.html
+title: WKB
+shortdesc: Example of using the WKB parser.
+docs: >
+ Create features from geometries in WKB (Well Known Binary) format.
+tags: "wkb, well known binary"
+---
+
diff --git a/examples/wkb.js b/examples/wkb.js
new file mode 100644
index 0000000000..dc04b6ed6e
--- /dev/null
+++ b/examples/wkb.js
@@ -0,0 +1,34 @@
+import Map from '../src/ol/Map.js';
+import View from '../src/ol/View.js';
+import WKB from '../src/ol/format/WKB.js';
+import {OSM, Vector as VectorSource} from '../src/ol/source.js';
+import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
+
+const raster = new TileLayer({
+ source: new OSM(),
+});
+
+const wkb =
+ '0103000000010000000500000054E3A59BC4602540643BDF4F8D1739C05C8FC2F5284C4140EC51B81E852B34C0D578E926316843406F1283C0CAD141C01B2FDD2406012B40A4703D0AD79343C054E3A59BC4602540643BDF4F8D1739C0';
+
+const format = new WKB();
+
+const feature = format.readFeature(wkb, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+});
+
+const vector = new VectorLayer({
+ source: new VectorSource({
+ features: [feature],
+ }),
+});
+
+const map = new Map({
+ layers: [raster, vector],
+ target: 'map',
+ view: new View({
+ center: [2952104.0199, -3277504.823],
+ zoom: 4,
+ }),
+});
diff --git a/src/ol/format/Feature.js b/src/ol/format/Feature.js
index 67cecd902c..9f629c776a 100644
--- a/src/ol/format/Feature.js
+++ b/src/ol/format/Feature.js
@@ -187,7 +187,7 @@ class FeatureFormat {
* @abstract
* @param {import("../Feature.js").default} feature Feature.
* @param {WriteOptions} [opt_options] Write options.
- * @return {string} Result.
+ * @return {string|ArrayBuffer} Result.
*/
writeFeature(feature, opt_options) {
return abstract();
@@ -199,7 +199,7 @@ class FeatureFormat {
* @abstract
* @param {Array} features Features.
* @param {WriteOptions} [opt_options] Write options.
- * @return {string} Result.
+ * @return {string|ArrayBuffer} Result.
*/
writeFeatures(features, opt_options) {
return abstract();
@@ -211,7 +211,7 @@ class FeatureFormat {
* @abstract
* @param {import("../geom/Geometry.js").default} geometry Geometry.
* @param {WriteOptions} [opt_options] Write options.
- * @return {string} Result.
+ * @return {string|ArrayBuffer} Result.
*/
writeGeometry(geometry, opt_options) {
return abstract();
diff --git a/src/ol/format/WKB.js b/src/ol/format/WKB.js
new file mode 100644
index 0000000000..831fd3c800
--- /dev/null
+++ b/src/ol/format/WKB.js
@@ -0,0 +1,885 @@
+/**
+ * @module ol/format/WKB
+ */
+import Feature from '../Feature.js';
+import FeatureFormat, {transformGeometryWithOptions} from './Feature.js';
+import FormatType from './FormatType.js';
+import GeometryCollection from '../geom/GeometryCollection.js';
+import GeometryLayout from '../geom/GeometryLayout.js';
+import GeometryType from '../geom/GeometryType.js';
+import LineString from '../geom/LineString.js';
+import MultiLineString from '../geom/MultiLineString.js';
+import MultiPoint from '../geom/MultiPoint.js';
+import MultiPolygon from '../geom/MultiPolygon.js';
+import Point from '../geom/Point.js';
+import Polygon from '../geom/Polygon.js';
+import {get as getProjection} from '../proj.js';
+
+import SimpleGeometry from '../geom/SimpleGeometry.js';
+import {assign} from '../obj.js';
+
+// WKB spec: https://www.ogc.org/standards/sfa
+// EWKB spec: https://raw.githubusercontent.com/postgis/postgis/2.1.0/doc/ZMSgeoms.txt
+
+/**
+ * @const
+ * @enum {number}
+ */
+const WKBGeometryType = {
+ POINT: 1,
+ LINE_STRING: 2,
+ POLYGON: 3,
+ MULTI_POINT: 4,
+ MULTI_LINE_STRING: 5,
+ MULTI_POLYGON: 6,
+ GEOMETRY_COLLECTION: 7,
+
+ /*
+ CIRCULAR_STRING: 8,
+ COMPOUND_CURVE: 9,
+ CURVE_POLYGON: 10,
+
+ MULTI_CURVE: 11,
+ MULTI_SURFACE: 12,
+ CURVE: 13,
+ SURFACE: 14,
+ */
+
+ POLYHEDRAL_SURFACE: 15,
+ TIN: 16,
+ TRIANGLE: 17,
+};
+
+class WkbReader {
+ /**
+ * @param {DataView} view source to read
+ */
+ constructor(view) {
+ this.view_ = view;
+ this.pos_ = 0;
+
+ this.initialized_ = false;
+ this.isLittleEndian_ = false;
+ this.hasZ_ = false;
+ this.hasM_ = false;
+ /** @type {number} */
+ this.srid_ = null;
+
+ this.layout_ = GeometryLayout.XY;
+ }
+
+ /**
+ * @return {number} value
+ */
+ readUint8() {
+ return this.view_.getUint8(this.pos_++);
+ }
+
+ /**
+ * @param {boolean} [isLittleEndian] Whether read value as little endian
+ * @return {number} value
+ */
+ readUint32(isLittleEndian) {
+ return this.view_.getUint32(
+ (this.pos_ += 4) - 4,
+ isLittleEndian !== undefined ? isLittleEndian : this.isLittleEndian_
+ );
+ }
+
+ /**
+ * @param {boolean} [isLittleEndian] Whether read value as little endian
+ * @return {number} value
+ */
+ readDouble(isLittleEndian) {
+ return this.view_.getFloat64(
+ (this.pos_ += 8) - 8,
+ isLittleEndian !== undefined ? isLittleEndian : this.isLittleEndian_
+ );
+ }
+
+ /**
+ * @return {import('../coordinate.js').Coordinate} coords for Point
+ */
+ readPoint() {
+ /** @type import('../coordinate.js').Coordinate */
+ const coords = [];
+
+ coords.push(this.readDouble());
+ coords.push(this.readDouble());
+ if (this.hasZ_) {
+ coords.push(this.readDouble());
+ }
+ if (this.hasM_) {
+ coords.push(this.readDouble());
+ }
+
+ return coords;
+ }
+
+ /**
+ * @return {Array} coords for LineString / LinearRing
+ */
+ readLineString() {
+ const numPoints = this.readUint32();
+
+ /** @type Array */
+ const coords = [];
+ for (let i = 0; i < numPoints; i++) {
+ coords.push(this.readPoint());
+ }
+
+ return coords;
+ }
+
+ /**
+ * @return {Array>} coords for Polygon like
+ */
+ readPolygon() {
+ const numRings = this.readUint32();
+
+ /** @type Array> */
+ const rings = [];
+ for (let i = 0; i < numRings; i++) {
+ rings.push(this.readLineString()); // as a LinearRing
+ }
+
+ return rings;
+ }
+
+ /**
+ * @param {number} [expectedTypeId] Expected WKB Type ID
+ * @return {number} WKB Type ID
+ */
+ readWkbHeader(expectedTypeId) {
+ const byteOrder = this.readUint8();
+ const isLittleEndian = byteOrder > 0;
+
+ const wkbType = this.readUint32(isLittleEndian);
+ const wkbTypeThousandth = Math.floor((wkbType & 0x0fffffff) / 1000);
+ const hasZ =
+ Boolean(wkbType & 0x80000000) ||
+ wkbTypeThousandth === 1 ||
+ wkbTypeThousandth === 3;
+ const hasM =
+ Boolean(wkbType & 0x40000000) ||
+ wkbTypeThousandth === 2 ||
+ wkbTypeThousandth === 3;
+ const hasSRID = Boolean(wkbType & 0x20000000);
+ const typeId = (wkbType & 0x0fffffff) % 1000; // Assume 1000 is an upper limit for type ID
+ const layout = ['XY', hasZ ? 'Z' : '', hasM ? 'M' : ''].join('');
+
+ const srid = hasSRID ? this.readUint32(isLittleEndian) : null;
+
+ if (expectedTypeId !== undefined && expectedTypeId !== typeId) {
+ throw new Error('Unexpected WKB geometry type ' + typeId);
+ }
+
+ if (this.initialized_) {
+ // sanity checks
+ if (this.isLittleEndian_ !== isLittleEndian) {
+ throw new Error('Inconsistent endian');
+ }
+ if (this.layout_ !== layout) {
+ throw new Error('Inconsistent geometry layout');
+ }
+ if (srid && this.srid_ !== srid) {
+ throw new Error('Inconsistent coordinate system (SRID)');
+ }
+ } else {
+ this.isLittleEndian_ = isLittleEndian;
+ this.hasZ_ = hasZ;
+ this.hasM_ = hasM;
+ this.layout_ = layout;
+ this.srid_ = srid;
+ this.initialized_ = true;
+ }
+
+ return typeId;
+ }
+
+ /**
+ * @param {number} typeId WKB Type ID
+ * @return {any} values read
+ */
+ readWkbPayload(typeId) {
+ switch (typeId) {
+ case WKBGeometryType.POINT:
+ return this.readPoint();
+
+ case WKBGeometryType.LINE_STRING:
+ return this.readLineString();
+
+ case WKBGeometryType.POLYGON:
+ case WKBGeometryType.TRIANGLE:
+ return this.readPolygon();
+
+ case WKBGeometryType.MULTI_POINT:
+ return this.readMultiPoint();
+
+ case WKBGeometryType.MULTI_LINE_STRING:
+ return this.readMultiLineString();
+
+ case WKBGeometryType.MULTI_POLYGON:
+ case WKBGeometryType.POLYHEDRAL_SURFACE:
+ case WKBGeometryType.TIN:
+ return this.readMultiPolygon();
+
+ case WKBGeometryType.GEOMETRY_COLLECTION:
+ return this.readGeometryCollection();
+
+ default:
+ throw new Error(
+ 'Unsupported WKB geometry type ' + typeId + ' is found'
+ );
+ }
+ }
+
+ /**
+ * @param {number} expectedTypeId Expected WKB Type ID
+ * @return {any} values read
+ */
+ readWkbBlock(expectedTypeId) {
+ return this.readWkbPayload(this.readWkbHeader(expectedTypeId));
+ }
+
+ /**
+ * @param {Function} reader reader function for each item
+ * @param {number} [expectedTypeId] Expected WKB Type ID
+ * @return {any} values read
+ */
+ readWkbCollection(reader, expectedTypeId) {
+ const num = this.readUint32();
+
+ const items = [];
+ for (let i = 0; i < num; i++) {
+ const result = reader.call(this, expectedTypeId);
+ if (result) {
+ items.push(result);
+ }
+ }
+
+ return items;
+ }
+
+ /**
+ * @return {Array} coords for MultiPoint
+ */
+ readMultiPoint() {
+ return this.readWkbCollection(this.readWkbBlock, WKBGeometryType.POINT);
+ }
+
+ /**
+ * @return {Array>} coords for MultiLineString like
+ */
+ readMultiLineString() {
+ return this.readWkbCollection(
+ this.readWkbBlock,
+ WKBGeometryType.LINE_STRING
+ );
+ }
+
+ /**
+ * @return {Array>>} coords for MultiPolygon like
+ */
+ readMultiPolygon() {
+ return this.readWkbCollection(this.readWkbBlock, WKBGeometryType.POLYGON);
+ }
+
+ /**
+ * @return {Array} array of geometries
+ */
+ readGeometryCollection() {
+ return this.readWkbCollection(this.readGeometry);
+ }
+
+ /**
+ * @return {import('../geom/Geometry.js').default} geometry
+ */
+ readGeometry() {
+ const typeId = this.readWkbHeader();
+ const result = this.readWkbPayload(typeId);
+
+ switch (typeId) {
+ case WKBGeometryType.POINT:
+ return new Point(
+ /** @type {import('../coordinate.js').Coordinate} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.LINE_STRING:
+ return new LineString(
+ /** @type {Array} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.POLYGON:
+ case WKBGeometryType.TRIANGLE:
+ return new Polygon(
+ /** @type {Array>} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.MULTI_POINT:
+ return new MultiPoint(
+ /** @type {Array} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.MULTI_LINE_STRING:
+ return new MultiLineString(
+ /** @type {Array>} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.MULTI_POLYGON:
+ case WKBGeometryType.POLYHEDRAL_SURFACE:
+ case WKBGeometryType.TIN:
+ return new MultiPolygon(
+ /** @type {Array>>} */ (result),
+ this.layout_
+ );
+
+ case WKBGeometryType.GEOMETRY_COLLECTION:
+ return new GeometryCollection(
+ /** @type {Array} */ (result)
+ );
+
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * @return {number} SRID in the EWKB. `null` if not defined.
+ */
+ getSrid() {
+ return this.srid_;
+ }
+}
+
+class WkbWriter {
+ /**
+ * @type {object}
+ * @property {string} [layout] geometryLayout
+ * @property {boolean} [littleEndian=true] littleEndian
+ * @property {boolean} [ewkb=true] Whether writes in EWKB format
+ * @property {object} [nodata] NoData value for each axes
+ * @param {object} opts options
+ */
+ constructor(opts) {
+ opts = opts || {};
+
+ /** @type {string} */
+ this.layout_ = opts.layout;
+ this.isLittleEndian_ = opts.littleEndian !== false;
+
+ this.isEWKB_ = opts.ewkb !== false;
+
+ /** @type {Array>} */
+ this.writeQueue_ = [];
+
+ /**
+ * @type {object}
+ * @property {number} X NoData value for X
+ * @property {number} Y NoData value for Y
+ * @property {number} Z NoData value for Z
+ * @property {number} M NoData value for M
+ */
+ this.nodata_ = assign({X: 0, Y: 0, Z: 0, M: 0}, opts.nodata);
+ }
+
+ /**
+ * @param {number} value value
+ */
+ writeUint8(value) {
+ this.writeQueue_.push([1, value]);
+ }
+
+ /**
+ * @param {number} value value
+ */
+ writeUint32(value) {
+ this.writeQueue_.push([4, value]);
+ }
+
+ /**
+ * @param {number} value value
+ */
+ writeDouble(value) {
+ this.writeQueue_.push([8, value]);
+ }
+
+ /**
+ * @param {import('../coordinate.js').Coordinate} coords coords
+ * @param {import("../geom/GeometryLayout").default} layout layout
+ */
+ writePoint(coords, layout) {
+ /**
+ * @type {object}
+ * @property {number} X NoData value for X
+ * @property {number} Y NoData value for Y
+ * @property {number} [Z] NoData value for Z
+ * @property {number} [M] NoData value for M
+ */
+ const coordsObj = assign.apply(
+ null,
+ layout.split('').map((axis, idx) => ({[axis]: coords[idx]}))
+ );
+
+ for (const axis of this.layout_) {
+ this.writeDouble(
+ axis in coordsObj ? coordsObj[axis] : this.nodata_[axis]
+ );
+ }
+ }
+
+ /**
+ * @param {Array} coords coords
+ * @param {import("../geom/GeometryLayout").default} layout layout
+ */
+ writeLineString(coords, layout) {
+ this.writeUint32(coords.length); // numPoints
+ for (let i = 0; i < coords.length; i++) {
+ this.writePoint(coords[i], layout);
+ }
+ }
+
+ /**
+ * @param {Array>} rings rings
+ * @param {import("../geom/GeometryLayout").default} layout layout
+ */
+ writePolygon(rings, layout) {
+ this.writeUint32(rings.length); // numRings
+ for (let i = 0; i < rings.length; i++) {
+ this.writeLineString(rings[i], layout); // as a LinearRing
+ }
+ }
+
+ /**
+ * @param {number} wkbType WKB Type ID
+ * @param {number} [srid] SRID
+ */
+ writeWkbHeader(wkbType, srid) {
+ wkbType %= 1000; // Assume 1000 is an upper limit for type ID
+ if (this.layout_.indexOf('Z') >= 0) {
+ wkbType += this.isEWKB_ ? 0x80000000 : 1000;
+ }
+ if (this.layout_.indexOf('M') >= 0) {
+ wkbType += this.isEWKB_ ? 0x40000000 : 2000;
+ }
+ if (this.isEWKB_ && Number.isInteger(srid)) {
+ wkbType |= 0x20000000;
+ }
+
+ this.writeUint8(this.isLittleEndian_ ? 1 : 0);
+ this.writeUint32(wkbType);
+ if (this.isEWKB_ && Number.isInteger(srid)) {
+ this.writeUint32(srid);
+ }
+ }
+
+ /**
+ * @param {Array} coords coords
+ * @param {string} layout layout
+ */
+ writeMultiPoint(coords, layout) {
+ this.writeUint32(coords.length); // numItems
+ for (let i = 0; i < coords.length; i++) {
+ this.writeWkbHeader(1);
+ this.writePoint(coords[i], layout);
+ }
+ }
+
+ /**
+ * @param {Array>} coords coords
+ * @param {string} layout layout
+ */
+ writeMultiLineString(coords, layout) {
+ this.writeUint32(coords.length); // numItems
+ for (let i = 0; i < coords.length; i++) {
+ this.writeWkbHeader(2);
+ this.writeLineString(coords[i], layout);
+ }
+ }
+
+ /**
+ * @param {Array>>} coords coords
+ * @param {string} layout layout
+ */
+ writeMultiPolygon(coords, layout) {
+ this.writeUint32(coords.length); // numItems
+ for (let i = 0; i < coords.length; i++) {
+ this.writeWkbHeader(3);
+ this.writePolygon(coords[i], layout);
+ }
+ }
+
+ /**
+ * @param {Array} geometries geometries
+ */
+ writeGeometryCollection(geometries) {
+ this.writeUint32(geometries.length); // numItems
+
+ for (let i = 0; i < geometries.length; i++) {
+ this.writeGeometry(geometries[i]);
+ }
+ }
+
+ /**
+ * @param {import("../geom/Geometry.js").default} geom geometry
+ * @param {import("../geom/GeometryLayout.js").default} [layout] layout
+ * @return {import("../geom/GeometryLayout.js").default} minumum layout made by common axes
+ */
+ findMinimumLayout(geom, layout = GeometryLayout.XYZM) {
+ /**
+ * @param {import("../geom/GeometryLayout.js").default} a A
+ * @param {import("../geom/GeometryLayout.js").default} b B
+ * @return {import("../geom/GeometryLayout.js").default} minumum layout made by common axes
+ */
+ const GeometryLayout_min = (a, b) => {
+ if (a === b) {
+ return a;
+ }
+
+ if (a === GeometryLayout.XYZM) {
+ // anything `b` is minimum
+ return b;
+ }
+ if (b === GeometryLayout.XYZM) {
+ // anything `a` is minimum
+ return a;
+ }
+
+ // otherwise, incompatible
+ return GeometryLayout.XY;
+ };
+
+ if (geom instanceof SimpleGeometry) {
+ return GeometryLayout_min(geom.getLayout(), layout);
+ }
+
+ if (geom instanceof GeometryCollection) {
+ const geoms = geom.getGeometriesArray();
+ for (let i = 0; i < geoms.length && layout !== GeometryLayout.XY; i++) {
+ layout = this.findMinimumLayout(geoms[i], layout);
+ }
+ }
+
+ return layout;
+ }
+
+ /**
+ * @param {import("../geom/Geometry.js").default} geom geometry
+ * @param {number} [srid] SRID
+ */
+ writeGeometry(geom, srid) {
+ const wkblut = {
+ [GeometryType.POINT]: WKBGeometryType.POINT,
+ [GeometryType.LINE_STRING]: WKBGeometryType.LINE_STRING,
+ [GeometryType.POLYGON]: WKBGeometryType.POLYGON,
+ [GeometryType.MULTI_POINT]: WKBGeometryType.MULTI_POINT,
+ [GeometryType.MULTI_LINE_STRING]: WKBGeometryType.MULTI_LINE_STRING,
+ [GeometryType.MULTI_POLYGON]: WKBGeometryType.MULTI_POLYGON,
+ [GeometryType.GEOMETRY_COLLECTION]: WKBGeometryType.GEOMETRY_COLLECTION,
+ };
+ const geomType = geom.getType();
+ const typeId = wkblut[geomType];
+
+ if (!typeId) {
+ throw new Error('GeometryType ' + geomType + ' is not supported');
+ }
+
+ // first call of writeGeometry() traverse whole geometries to determine its output layout if not specified on constructor.
+ if (!this.layout_) {
+ this.layout_ = this.findMinimumLayout(geom);
+ }
+
+ this.writeWkbHeader(typeId, srid);
+
+ if (geom instanceof SimpleGeometry) {
+ const writerLUT = {
+ [GeometryType.POINT]: this.writePoint,
+ [GeometryType.LINE_STRING]: this.writeLineString,
+ [GeometryType.POLYGON]: this.writePolygon,
+ [GeometryType.MULTI_POINT]: this.writeMultiPoint,
+ [GeometryType.MULTI_LINE_STRING]: this.writeMultiLineString,
+ [GeometryType.MULTI_POLYGON]: this.writeMultiPolygon,
+ };
+ writerLUT[geomType].call(this, geom.getCoordinates(), geom.getLayout());
+ } else if (geom instanceof GeometryCollection) {
+ this.writeGeometryCollection(geom.getGeometriesArray());
+ }
+ }
+
+ getBuffer() {
+ const byteLength = this.writeQueue_.reduce((acc, item) => acc + item[0], 0);
+ const buffer = new ArrayBuffer(byteLength);
+ const view = new DataView(buffer);
+
+ let pos = 0;
+ this.writeQueue_.forEach((item) => {
+ switch (item[0]) {
+ case 1:
+ view.setUint8(pos, item[1]);
+ break;
+ case 4:
+ view.setUint32(pos, item[1], this.isLittleEndian_);
+ break;
+ case 8:
+ view.setFloat64(pos, item[1], this.isLittleEndian_);
+ break;
+ default:
+ break;
+ }
+
+ pos += item[0];
+ });
+
+ return buffer;
+ }
+}
+
+/**
+ * @typedef {Object} Options
+ * @property {boolean} [splitCollection=false] Whether to split GeometryCollections into multiple features on reading.
+ * @property {boolean} [hex=true] Returns hex string instead of ArrayBuffer for output. This also is used as a hint internally whether it should load contents as text or ArrayBuffer on reading.
+ * @property {boolean} [littleEndian=true] Use littleEndian for output.
+ * @property {boolean} [ewkb=true] Use EWKB format for output.
+ * @property {import("../geom/GeometryLayout").default} [geometryLayout=null] Use specific coordinate layout for output features (null: auto detect)
+ * @property {number} [nodataZ=0] If the `geometryLayout` doesn't match with geometry to be output, this value is used to fill missing coordinate value of Z.
+ * @property {number} [nodataM=0] If the `geometryLayout` doesn't match with geometry to be output, this value is used to fill missing coordinate value of M.
+ * @property {number|boolean} [srid=true] SRID for output. Specify integer value to enforce the value as a SRID. Specify `true` to extract from `dataProjection`. `false` to suppress the output. This option only takes effect when `ewkb` is `true`.
+ */
+
+/**
+ * @classdesc
+ * Geometry format for reading and writing data in the `Well-Known Binary` (WKB) format.
+ * Also supports `Extended Well-Known Binary` (EWKB) format, used in PostGIS for example.
+ *
+ * @api
+ */
+class WKB extends FeatureFormat {
+ /**
+ * @param {Options} [opt_options] Optional configuration object.
+ */
+ constructor(opt_options) {
+ super();
+
+ const options = opt_options ? opt_options : {};
+
+ this.splitCollection = Boolean(options.splitCollection);
+
+ this.viewCache_ = null;
+
+ this.hex_ = options.hex !== false;
+ this.littleEndian_ = options.littleEndian !== false;
+ this.ewkb_ = options.ewkb !== false;
+
+ this.layout_ = options.geometryLayout; // null for auto detect
+ this.nodataZ_ = options.nodataZ || 0;
+ this.nodataM_ = options.nodataM || 0;
+
+ this.srid_ = options.srid;
+ }
+
+ /**
+ * @return {import("./FormatType.js").default} Format.
+ */
+ getType() {
+ return this.hex_ ? FormatType.TEXT : FormatType.ARRAY_BUFFER;
+ }
+
+ /**
+ * Read a single feature from a source.
+ *
+ * @param {string|ArrayBuffer|ArrayBufferView} source Source.
+ * @param {import("./Feature.js").ReadOptions} [opt_options] Read options.
+ * @return {import("../Feature.js").FeatureLike} Feature.
+ * @api
+ */
+ readFeature(source, opt_options) {
+ return new Feature({
+ geometry: this.readGeometry(source, opt_options),
+ });
+ }
+
+ /**
+ * Read all features from a source.
+ *
+ * @param {string|ArrayBuffer|ArrayBufferView} source Source.
+ * @param {import("./Feature.js").ReadOptions} [opt_options] Read options.
+ * @return {Array} Features.
+ * @api
+ */
+ readFeatures(source, opt_options) {
+ let geometries = [];
+ const geometry = this.readGeometry(source, opt_options);
+ if (this.splitCollection && geometry instanceof GeometryCollection) {
+ geometries = geometry.getGeometriesArray();
+ } else {
+ geometries = [geometry];
+ }
+ return geometries.map((geometry) => new Feature({geometry}));
+ }
+
+ /**
+ * Read a single geometry from a source.
+ *
+ * @param {string|ArrayBuffer|ArrayBufferView} source Source.
+ * @param {import("./Feature.js").ReadOptions} [opt_options] Read options.
+ * @return {import("../geom/Geometry.js").default} Geometry.
+ * @api
+ */
+ readGeometry(source, opt_options) {
+ const view = getDataView(source);
+ if (!view) {
+ return null;
+ }
+
+ const reader = new WkbReader(view);
+ const geometry = reader.readGeometry();
+
+ this.viewCache_ = view; // cache for internal subsequent call of readProjection()
+ const options = this.getReadOptions(source, opt_options);
+ this.viewCache_ = null; // release
+
+ return transformGeometryWithOptions(geometry, false, options);
+ }
+
+ /**
+ * Read the projection from a source.
+ *
+ * @param {string|ArrayBuffer|ArrayBufferView} source Source.
+ * @return {import("../proj/Projection.js").default|undefined} Projection.
+ * @api
+ */
+ readProjection(source) {
+ const view = this.viewCache_ || getDataView(source);
+ if (!view) {
+ return undefined;
+ }
+
+ const reader = new WkbReader(view);
+ reader.readWkbHeader();
+
+ return (
+ (reader.getSrid() && getProjection('EPSG:' + reader.getSrid())) ||
+ undefined
+ );
+ }
+
+ /**
+ * Encode a feature in this format.
+ *
+ * @param {import("../Feature.js").default} feature Feature.
+ * @param {import("./Feature.js").WriteOptions} [opt_options] Write options.
+ * @return {string|ArrayBuffer} Result.
+ * @api
+ */
+ writeFeature(feature, opt_options) {
+ return this.writeGeometry(feature.getGeometry(), opt_options);
+ }
+
+ /**
+ * Encode an array of features in this format.
+ *
+ * @param {Array} features Features.
+ * @param {import("./Feature.js").WriteOptions} [opt_options] Write options.
+ * @return {string|ArrayBuffer} Result.
+ * @api
+ */
+ writeFeatures(features, opt_options) {
+ return this.writeGeometry(
+ new GeometryCollection(features.map((f) => f.getGeometry())),
+ opt_options
+ );
+ }
+
+ /**
+ * Write a single geometry in this format.
+ *
+ * @param {import("../geom/Geometry.js").default} geometry Geometry.
+ * @param {import("./Feature.js").WriteOptions} [opt_options] Write options.
+ * @return {string|ArrayBuffer} Result.
+ * @api
+ */
+ writeGeometry(geometry, opt_options) {
+ const options = this.adaptOptions(opt_options);
+
+ const writer = new WkbWriter({
+ layout: this.layout_,
+ littleEndian: this.littleEndian_,
+ ewkb: this.ewkb_,
+
+ nodata: {
+ Z: this.nodataZ_,
+ M: this.nodataM_,
+ },
+ });
+
+ // extract SRID from `dataProjection`
+ let srid = Number.isInteger(this.srid_) ? Number(this.srid_) : null;
+ if (this.srid_ !== false && !Number.isInteger(this.srid_)) {
+ const dataProjection =
+ options.dataProjection && getProjection(options.dataProjection);
+ if (dataProjection) {
+ const code = dataProjection.getCode();
+ if (code.indexOf('EPSG:') === 0) {
+ srid = Number(code.substring(5));
+ }
+ }
+ }
+
+ writer.writeGeometry(
+ transformGeometryWithOptions(geometry, true, options),
+ srid
+ );
+ const buffer = writer.getBuffer();
+
+ return this.hex_ ? encodeHexString(buffer) : buffer;
+ }
+}
+
+/**
+ * @param {ArrayBuffer} buffer source buffer
+ * @return {string} encoded hex string
+ */
+function encodeHexString(buffer) {
+ const view = new Uint8Array(buffer);
+ return Array.from(view.values())
+ .map((x) => (x < 16 ? '0' : '') + Number(x).toString(16).toUpperCase())
+ .join('');
+}
+
+/**
+ * @param {string} text source text
+ * @return {DataView} decoded binary buffer
+ */
+function decodeHexString(text) {
+ const buffer = new Uint8Array(text.length / 2);
+ for (let i = 0; i < text.length / 2; i++) {
+ buffer[i] = parseInt(text.substr(i * 2, 2), 16);
+ }
+ return new DataView(buffer.buffer);
+}
+
+/**
+ * @param {string | ArrayBuffer | ArrayBufferView} source source
+ * @return {DataView} data view
+ */
+function getDataView(source) {
+ if (typeof source === 'string') {
+ return decodeHexString(source);
+ } else if (ArrayBuffer.isView(source)) {
+ if (source instanceof DataView) {
+ return source;
+ }
+ return new DataView(source.buffer, source.byteOffset, source.byteLength);
+ } else if (source instanceof ArrayBuffer) {
+ return new DataView(source);
+ } else {
+ return null;
+ }
+}
+
+export default WKB;
diff --git a/test/spec/ol/format/wkb.test.js b/test/spec/ol/format/wkb.test.js
new file mode 100644
index 0000000000..dcd2bf51a2
--- /dev/null
+++ b/test/spec/ol/format/wkb.test.js
@@ -0,0 +1,1450 @@
+import Feature from '../../../../src/ol/Feature.js';
+import GeometryCollection from '../../../../src/ol/geom/GeometryCollection.js';
+import Point from '../../../../src/ol/geom/Point.js';
+import SimpleGeometry from '../../../../src/ol/geom/SimpleGeometry.js';
+import WKB from '../../../../src/ol/format/WKB.js';
+import WKT from '../../../../src/ol/format/WKT.js';
+import {transform} from '../../../../src/ol/proj.js';
+
+const patterns = [
+ [
+ '010100000000000000000024400000000000002440',
+ 'POINT(10 10)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000140240000000000004024000000000000',
+ 'POINT(10 10)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '01020000000300000000000000000024400000000000002440000000000000344000000000000034400000000000003e400000000000004440',
+ 'LINESTRING(10 10,20 20,30 40)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '0000000002000000034024000000000000402400000000000040340000000000004034000000000000403e0000000000004044000000000000',
+ 'LINESTRING(10 10,20 20,30 40)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '0103000000010000000500000000000000000024400000000000002440000000000000244000000000000034400000000000003440000000000000344000000000000034400000000000002e4000000000000024400000000000002440',
+ 'POLYGON((10 10,10 20,20 20,20 15,10 10))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000300000001000000054024000000000000402400000000000040240000000000004034000000000000403400000000000040340000000000004034000000000000402e00000000000040240000000000004024000000000000',
+ 'POLYGON((10 10,10 20,20 20,20 15,10 10))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010400000002000000010100000000000000000024400000000000002440010100000000000000000034400000000000003440',
+ 'MULTIPOINT(10 10,20 20)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000400000002000000000140240000000000004024000000000000000000000140340000000000004034000000000000',
+ 'MULTIPOINT(10 10,20 20)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '01050000000200000001020000000200000000000000000024400000000000002440000000000000344000000000000034400102000000020000000000000000002e400000000000002e400000000000003e400000000000002e40',
+ 'MULTILINESTRING((10 10,20 20),(15 15,30 15))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '0000000005000000020000000002000000024024000000000000402400000000000040340000000000004034000000000000000000000200000002402e000000000000402e000000000000403e000000000000402e000000000000',
+ 'MULTILINESTRING((10 10,20 20),(15 15,30 15))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '0106000000020000000103000000010000000500000000000000000024400000000000002440000000000000244000000000000034400000000000003440000000000000344000000000000034400000000000002e4000000000000024400000000000002440010300000001000000040000000000000000004e400000000000004e400000000000805140000000000080514000000000000054400000000000004e400000000000004e400000000000004e40',
+ 'MULTIPOLYGON(((10 10,10 20,20 20,20 15,10 10)),((60 60,70 70,80 60,60 60)))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000600000002000000000300000001000000054024000000000000402400000000000040240000000000004034000000000000403400000000000040340000000000004034000000000000402e0000000000004024000000000000402400000000000000000000030000000100000004404e000000000000404e000000000000405180000000000040518000000000004054000000000000404e000000000000404e000000000000404e000000000000',
+ 'MULTIPOLYGON(((10 10,10 20,20 20,20 15,10 10)),((60 60,70 70,80 60,60 60)))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '01070000000300000001010000000000000000002440000000000000244001010000000000000000003e400000000000003e400102000000020000000000000000002e400000000000002e4000000000000034400000000000003440',
+ 'GEOMETRYCOLLECTION(POINT(10 10),POINT(30 30),LINESTRING(15 15,20 20))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '0000000007000000030000000001402400000000000040240000000000000000000001403e000000000000403e000000000000000000000200000002402e000000000000402e00000000000040340000000000004034000000000000',
+ 'GEOMETRYCOLLECTION(POINT(10 10),POINT(30 30),LINESTRING(15 15,20 20))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+
+ [
+ '01e9030000000000000000244000000000000024400000000000001440',
+ 'POINT Z (10 10 5)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003e9402400000000000040240000000000004014000000000000',
+ 'POINT Z (10 10 5)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0101000080000000000000244000000000000024400000000000001440',
+ 'POINT Z (10 10 5)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0080000001402400000000000040240000000000004014000000000000',
+ 'POINT Z (10 10 5)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ea030000030000000000000000002440000000000000244000000000000014400000000000003440000000000000344000000000000010400000000000003e4000000000000044400000000000000840',
+ 'LINESTRING Z (10 10 5,20 20 4,30 40 3)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ea00000003402400000000000040240000000000004014000000000000403400000000000040340000000000004010000000000000403e00000000000040440000000000004008000000000000',
+ 'LINESTRING Z (10 10 5,20 20 4,30 40 3)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0102000080030000000000000000002440000000000000244000000000000014400000000000003440000000000000344000000000000010400000000000003e4000000000000044400000000000000840',
+ 'LINESTRING Z (10 10 5,20 20 4,30 40 3)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000200000003402400000000000040240000000000004014000000000000403400000000000040340000000000004010000000000000403e00000000000040440000000000004008000000000000',
+ 'LINESTRING Z (10 10 5,20 20 4,30 40 3)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01eb030000010000000500000000000000000024400000000000002440000000000000144000000000000024400000000000003440000000000000104000000000000034400000000000003440000000000000084000000000000034400000000000002e40000000000000004000000000000024400000000000002440000000000000f03f',
+ 'POLYGON Z ((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003eb00000001000000054024000000000000402400000000000040140000000000004024000000000000403400000000000040100000000000004034000000000000403400000000000040080000000000004034000000000000402e0000000000004000000000000000402400000000000040240000000000003ff0000000000000',
+ 'POLYGON Z ((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0103000080010000000500000000000000000024400000000000002440000000000000144000000000000024400000000000003440000000000000104000000000000034400000000000003440000000000000084000000000000034400000000000002e40000000000000004000000000000024400000000000002440000000000000f03f',
+ 'POLYGON Z ((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000300000001000000054024000000000000402400000000000040140000000000004024000000000000403400000000000040100000000000004034000000000000403400000000000040080000000000004034000000000000402e0000000000004000000000000000402400000000000040240000000000003ff0000000000000',
+ 'POLYGON Z ((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ec0300000200000001e903000000000000000024400000000000002440000000000000144001e9030000000000000000344000000000000034400000000000001440',
+ 'MULTIPOINT Z (10 10 5,20 20 5)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ec0000000200000003e940240000000000004024000000000000401400000000000000000003e9403400000000000040340000000000004014000000000000',
+ 'MULTIPOINT Z (10 10 5,20 20 5)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01040000800200000001010000800000000000002440000000000000244000000000000014400101000080000000000000344000000000000034400000000000001440',
+ 'MULTIPOINT Z (10 10 5,20 20 5)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00800000040000000200800000014024000000000000402400000000000040140000000000000080000001403400000000000040340000000000004014000000000000',
+ 'MULTIPOINT Z (10 10 5,20 20 5)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ed0300000200000001ea0300000200000000000000000024400000000000002440000000000000144000000000000034400000000000003440000000000000104001ea030000020000000000000000002e400000000000002e4000000000000008400000000000003e400000000000002e400000000000000040',
+ 'MULTILINESTRING Z ((10 10 5,20 20 4),(15 15 3,30 15 2))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ed0000000200000003ea0000000240240000000000004024000000000000401400000000000040340000000000004034000000000000401000000000000000000003ea00000002402e000000000000402e0000000000004008000000000000403e000000000000402e0000000000004000000000000000',
+ 'MULTILINESTRING Z ((10 10 5,20 20 4),(15 15 3,30 15 2))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0105000080020000000102000080020000000000000000002440000000000000244000000000000014400000000000003440000000000000344000000000000010400102000080020000000000000000002e400000000000002e4000000000000008400000000000003e400000000000002e400000000000000040',
+ 'MULTILINESTRING Z ((10 10 5,20 20 4),(15 15 3,30 15 2))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000500000002008000000200000002402400000000000040240000000000004014000000000000403400000000000040340000000000004010000000000000008000000200000002402e000000000000402e0000000000004008000000000000403e000000000000402e0000000000004000000000000000',
+ 'MULTILINESTRING Z ((10 10 5,20 20 4),(15 15 3,30 15 2))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ee0300000200000001eb030000010000000500000000000000000024400000000000002440000000000000144000000000000024400000000000003440000000000000104000000000000034400000000000003440000000000000084000000000000034400000000000002e40000000000000004000000000000024400000000000002440000000000000f03f01eb03000001000000040000000000000000004e400000000000004e40000000000000144000000000008051400000000000805140000000000000104000000000000054400000000000004e4000000000000008400000000000004e400000000000004e400000000000000040',
+ 'MULTIPOLYGON Z (((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1)),((60 60 5,70 70 4,80 60 3,60 60 2)))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ee0000000200000003eb00000001000000054024000000000000402400000000000040140000000000004024000000000000403400000000000040100000000000004034000000000000403400000000000040080000000000004034000000000000402e0000000000004000000000000000402400000000000040240000000000003ff000000000000000000003eb0000000100000004404e000000000000404e00000000000040140000000000004051800000000000405180000000000040100000000000004054000000000000404e0000000000004008000000000000404e000000000000404e0000000000004000000000000000',
+ 'MULTIPOLYGON Z (((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1)),((60 60 5,70 70 4,80 60 3,60 60 2)))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0106000080020000000103000080010000000500000000000000000024400000000000002440000000000000144000000000000024400000000000003440000000000000104000000000000034400000000000003440000000000000084000000000000034400000000000002e40000000000000004000000000000024400000000000002440000000000000f03f010300008001000000040000000000000000004e400000000000004e40000000000000144000000000008051400000000000805140000000000000104000000000000054400000000000004e4000000000000008400000000000004e400000000000004e400000000000000040',
+ 'MULTIPOLYGON Z (((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1)),((60 60 5,70 70 4,80 60 3,60 60 2)))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000600000002008000000300000001000000054024000000000000402400000000000040140000000000004024000000000000403400000000000040100000000000004034000000000000403400000000000040080000000000004034000000000000402e0000000000004000000000000000402400000000000040240000000000003ff000000000000000800000030000000100000004404e000000000000404e00000000000040140000000000004051800000000000405180000000000040100000000000004054000000000000404e0000000000004008000000000000404e000000000000404e0000000000004000000000000000',
+ 'MULTIPOLYGON Z (((10 10 5,10 20 4,20 20 3,20 15 2,10 10 1)),((60 60 5,70 70 4,80 60 3,60 60 2)))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ef0300000300000001e903000000000000000024400000000000002440000000000000144001e90300000000000000003e400000000000003e40000000000000144001ea030000020000000000000000002e400000000000002e400000000000001440000000000000344000000000000034400000000000001440',
+ 'GEOMETRYCOLLECTION Z (POINT Z (10 10 5),POINT Z (30 30 5),LINESTRING Z (15 15 5,20 20 5))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ef0000000300000003e940240000000000004024000000000000401400000000000000000003e9403e000000000000403e000000000000401400000000000000000003ea00000002402e000000000000402e0000000000004014000000000000403400000000000040340000000000004014000000000000',
+ 'GEOMETRYCOLLECTION Z (POINT Z (10 10 5),POINT Z (30 30 5),LINESTRING Z (15 15 5,20 20 5))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010700008003000000010100008000000000000024400000000000002440000000000000144001010000800000000000003e400000000000003e4000000000000014400102000080020000000000000000002e400000000000002e400000000000001440000000000000344000000000000034400000000000001440',
+ 'GEOMETRYCOLLECTION Z (POINT Z (10 10 5),POINT Z (30 30 5),LINESTRING Z (15 15 5,20 20 5))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00800000070000000300800000014024000000000000402400000000000040140000000000000080000001403e000000000000403e0000000000004014000000000000008000000200000002402e000000000000402e0000000000004014000000000000403400000000000040340000000000004014000000000000',
+ 'GEOMETRYCOLLECTION Z (POINT Z (10 10 5),POINT Z (30 30 5),LINESTRING Z (15 15 5,20 20 5))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+
+ [
+ '01d1070000000000000000244000000000000024400000000000804640',
+ 'POINT M (10 10 45)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d1402400000000000040240000000000004046800000000000',
+ 'POINT M (10 10 45)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0101000040000000000000244000000000000024400000000000804640',
+ 'POINT M (10 10 45)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '0040000001402400000000000040240000000000004046800000000000',
+ 'POINT M (10 10 45)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d2070000030000000000000000002440000000000000244000000000008046400000000000003440000000000000344000000000000046400000000000003e4000000000000044400000000000804540',
+ 'LINESTRING M (10 10 45,20 20 44,30 40 43)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d200000003402400000000000040240000000000004046800000000000403400000000000040340000000000004046000000000000403e00000000000040440000000000004045800000000000',
+ 'LINESTRING M (10 10 45,20 20 44,30 40 43)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0102000040030000000000000000002440000000000000244000000000008046400000000000003440000000000000344000000000000046400000000000003e4000000000000044400000000000804540',
+ 'LINESTRING M (10 10 45,20 20 44,30 40 43)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000200000003402400000000000040240000000000004046800000000000403400000000000040340000000000004046000000000000403e00000000000040440000000000004045800000000000',
+ 'LINESTRING M (10 10 45,20 20 44,30 40 43)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d3070000010000000500000000000000000024400000000000002440000000000080464000000000000024400000000000003440000000000000464000000000000034400000000000003440000000000080454000000000000034400000000000002e400000000000004540000000000000244000000000000024400000000000804440',
+ 'POLYGON M ((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d300000001000000054024000000000000402400000000000040468000000000004024000000000000403400000000000040460000000000004034000000000000403400000000000040458000000000004034000000000000402e0000000000004045000000000000402400000000000040240000000000004044800000000000',
+ 'POLYGON M ((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0103000040010000000500000000000000000024400000000000002440000000000080464000000000000024400000000000003440000000000000464000000000000034400000000000003440000000000080454000000000000034400000000000002e400000000000004540000000000000244000000000000024400000000000804440',
+ 'POLYGON M ((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000300000001000000054024000000000000402400000000000040468000000000004024000000000000403400000000000040460000000000004034000000000000403400000000000040458000000000004034000000000000402e0000000000004045000000000000402400000000000040240000000000004044800000000000',
+ 'POLYGON M ((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d40700000200000001d107000000000000000024400000000000002440000000000080464001d1070000000000000000344000000000000034400000000000804640',
+ 'MULTIPOINT M (10 10 45,20 20 45)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d40000000200000007d140240000000000004024000000000000404680000000000000000007d1403400000000000040340000000000004046800000000000',
+ 'MULTIPOINT M (10 10 45,20 20 45)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '01040000400200000001010000400000000000002440000000000000244000000000008046400101000040000000000000344000000000000034400000000000804640',
+ 'MULTIPOINT M (10 10 45,20 20 45)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '00400000040000000200400000014024000000000000402400000000000040468000000000000040000001403400000000000040340000000000004046800000000000',
+ 'MULTIPOINT M (10 10 45,20 20 45)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d50700000200000001d20700000200000000000000000024400000000000002440000000000080464000000000000034400000000000003440000000000000464001d2070000020000000000000000002e400000000000002e4000000000008045400000000000003e400000000000002e400000000000004540',
+ 'MULTILINESTRING M ((10 10 45,20 20 44),(15 15 43,30 15 42))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d50000000200000007d20000000240240000000000004024000000000000404680000000000040340000000000004034000000000000404600000000000000000007d200000002402e000000000000402e0000000000004045800000000000403e000000000000402e0000000000004045000000000000',
+ 'MULTILINESTRING M ((10 10 45,20 20 44),(15 15 43,30 15 42))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0105000040020000000102000040020000000000000000002440000000000000244000000000008046400000000000003440000000000000344000000000000046400102000040020000000000000000002e400000000000002e4000000000008045400000000000003e400000000000002e400000000000004540',
+ 'MULTILINESTRING M ((10 10 45,20 20 44),(15 15 43,30 15 42))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000500000002004000000200000002402400000000000040240000000000004046800000000000403400000000000040340000000000004046000000000000004000000200000002402e000000000000402e0000000000004045800000000000403e000000000000402e0000000000004045000000000000',
+ 'MULTILINESTRING M ((10 10 45,20 20 44),(15 15 43,30 15 42))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d60700000200000001d3070000010000000500000000000000000024400000000000002440000000000080464000000000000024400000000000003440000000000000464000000000000034400000000000003440000000000080454000000000000034400000000000002e40000000000000454000000000000024400000000000002440000000000080444001d307000001000000040000000000000000004e400000000000004e40000000000080464000000000008051400000000000805140000000000000464000000000000054400000000000004e4000000000008045400000000000004e400000000000004e400000000000004540',
+ 'MULTIPOLYGON M (((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41)),((60 60 45,70 70 44,80 60 43,60 60 42)))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d60000000200000007d300000001000000054024000000000000402400000000000040468000000000004024000000000000403400000000000040460000000000004034000000000000403400000000000040458000000000004034000000000000402e000000000000404500000000000040240000000000004024000000000000404480000000000000000007d30000000100000004404e000000000000404e00000000000040468000000000004051800000000000405180000000000040460000000000004054000000000000404e0000000000004045800000000000404e000000000000404e0000000000004045000000000000',
+ 'MULTIPOLYGON M (((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41)),((60 60 45,70 70 44,80 60 43,60 60 42)))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0106000040020000000103000040010000000500000000000000000024400000000000002440000000000080464000000000000024400000000000003440000000000000464000000000000034400000000000003440000000000080454000000000000034400000000000002e400000000000004540000000000000244000000000000024400000000000804440010300004001000000040000000000000000004e400000000000004e40000000000080464000000000008051400000000000805140000000000000464000000000000054400000000000004e4000000000008045400000000000004e400000000000004e400000000000004540',
+ 'MULTIPOLYGON M (((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41)),((60 60 45,70 70 44,80 60 43,60 60 42)))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000600000002004000000300000001000000054024000000000000402400000000000040468000000000004024000000000000403400000000000040460000000000004034000000000000403400000000000040458000000000004034000000000000402e000000000000404500000000000040240000000000004024000000000000404480000000000000400000030000000100000004404e000000000000404e00000000000040468000000000004051800000000000405180000000000040460000000000004054000000000000404e0000000000004045800000000000404e000000000000404e0000000000004045000000000000',
+ 'MULTIPOLYGON M (((10 10 45,10 20 44,20 20 43,20 15 42,10 10 41)),((60 60 45,70 70 44,80 60 43,60 60 42)))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d70700000300000001d107000000000000000024400000000000002440000000000080464001d10700000000000000003e400000000000003e40000000000080464001d2070000020000000000000000002e400000000000002e400000000000804640000000000000344000000000000034400000000000804640',
+ 'GEOMETRYCOLLECTION M (POINT M (10 10 45),POINT M (30 30 45),LINESTRING M (15 15 45,20 20 45))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d70000000300000007d140240000000000004024000000000000404680000000000000000007d1403e000000000000403e000000000000404680000000000000000007d200000002402e000000000000402e0000000000004046800000000000403400000000000040340000000000004046800000000000',
+ 'GEOMETRYCOLLECTION M (POINT M (10 10 45),POINT M (30 30 45),LINESTRING M (15 15 45,20 20 45))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010700004003000000010100004000000000000024400000000000002440000000000080464001010000400000000000003e400000000000003e4000000000008046400102000040020000000000000000002e400000000000002e400000000000804640000000000000344000000000000034400000000000804640',
+ 'GEOMETRYCOLLECTION M (POINT M (10 10 45),POINT M (30 30 45),LINESTRING M (15 15 45,20 20 45))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '00400000070000000300400000014024000000000000402400000000000040468000000000000040000001403e000000000000403e0000000000004046800000000000004000000200000002402e000000000000402e0000000000004046800000000000403400000000000040340000000000004046800000000000',
+ 'GEOMETRYCOLLECTION M (POINT M (10 10 45),POINT M (30 30 45),LINESTRING M (15 15 45,20 20 45))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+
+ [
+ '01b90b00000000000000002440000000000000244000000000000014400000000000804640',
+ 'POINT ZM (10 10 5 45)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bb94024000000000000402400000000000040140000000000004046800000000000',
+ 'POINT ZM (10 10 5 45)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01010000c00000000000002440000000000000244000000000000014400000000000804640',
+ 'POINT ZM (10 10 5 45)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000014024000000000000402400000000000040140000000000004046800000000000',
+ 'POINT ZM (10 10 5 45)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01ba0b000003000000000000000000244000000000000024400000000000001440000000000080464000000000000034400000000000003440000000000000104000000000000046400000000000003e40000000000000444000000000000008400000000000804540',
+ 'LINESTRING ZM (10 10 5 45,20 20 4 44,30 40 3 43)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bba0000000340240000000000004024000000000000401400000000000040468000000000004034000000000000403400000000000040100000000000004046000000000000403e000000000000404400000000000040080000000000004045800000000000',
+ 'LINESTRING ZM (10 10 5 45,20 20 4 44,30 40 3 43)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01020000c003000000000000000000244000000000000024400000000000001440000000000080464000000000000034400000000000003440000000000000104000000000000046400000000000003e40000000000000444000000000000008400000000000804540',
+ 'LINESTRING ZM (10 10 5 45,20 20 4 44,30 40 3 43)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000020000000340240000000000004024000000000000401400000000000040468000000000004034000000000000403400000000000040100000000000004046000000000000403e000000000000404400000000000040080000000000004045800000000000',
+ 'LINESTRING ZM (10 10 5 45,20 20 4 44,30 40 3 43)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bb0b0000010000000500000000000000000024400000000000002440000000000000144000000000008046400000000000002440000000000000344000000000000010400000000000004640000000000000344000000000000034400000000000000840000000000080454000000000000034400000000000002e400000000000000040000000000000454000000000000024400000000000002440000000000000f03f0000000000804440',
+ 'POLYGON ZM ((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbb00000001000000054024000000000000402400000000000040140000000000004046800000000000402400000000000040340000000000004010000000000000404600000000000040340000000000004034000000000000400800000000000040458000000000004034000000000000402e00000000000040000000000000004045000000000000402400000000000040240000000000003ff00000000000004044800000000000',
+ 'POLYGON ZM ((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01030000c0010000000500000000000000000024400000000000002440000000000000144000000000008046400000000000002440000000000000344000000000000010400000000000004640000000000000344000000000000034400000000000000840000000000080454000000000000034400000000000002e400000000000000040000000000000454000000000000024400000000000002440000000000000f03f0000000000804440',
+ 'POLYGON ZM ((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000300000001000000054024000000000000402400000000000040140000000000004046800000000000402400000000000040340000000000004010000000000000404600000000000040340000000000004034000000000000400800000000000040458000000000004034000000000000402e00000000000040000000000000004045000000000000402400000000000040240000000000003ff00000000000004044800000000000',
+ 'POLYGON ZM ((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bc0b00000200000001b90b0000000000000000244000000000000024400000000000001440000000000080464001b90b00000000000000003440000000000000344000000000000014400000000000804640',
+ 'MULTIPOINT ZM (10 10 5 45,20 20 5 45)',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbc000000020000000bb940240000000000004024000000000000401400000000000040468000000000000000000bb94034000000000000403400000000000040140000000000004046800000000000',
+ 'MULTIPOINT ZM (10 10 5 45,20 20 5 45)',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01040000c00200000001010000c0000000000000244000000000000024400000000000001440000000000080464001010000c00000000000003440000000000000344000000000000014400000000000804640',
+ 'MULTIPOINT ZM (10 10 5 45,20 20 5 45)',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000040000000200c0000001402400000000000040240000000000004014000000000000404680000000000000c00000014034000000000000403400000000000040140000000000004046800000000000',
+ 'MULTIPOINT ZM (10 10 5 45,20 20 5 45)',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bd0b00000200000001ba0b0000020000000000000000002440000000000000244000000000000014400000000000804640000000000000344000000000000034400000000000001040000000000000464001ba0b0000020000000000000000002e400000000000002e40000000000000084000000000008045400000000000003e400000000000002e4000000000000000400000000000004540',
+ 'MULTILINESTRING ZM ((10 10 5 45,20 20 4 44),(15 15 3 43,30 15 2 42))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbd000000020000000bba00000002402400000000000040240000000000004014000000000000404680000000000040340000000000004034000000000000401000000000000040460000000000000000000bba00000002402e000000000000402e00000000000040080000000000004045800000000000403e000000000000402e00000000000040000000000000004045000000000000',
+ 'MULTILINESTRING ZM ((10 10 5 45,20 20 4 44),(15 15 3 43,30 15 2 42))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01050000c00200000001020000c0020000000000000000002440000000000000244000000000000014400000000000804640000000000000344000000000000034400000000000001040000000000000464001020000c0020000000000000000002e400000000000002e40000000000000084000000000008045400000000000003e400000000000002e4000000000000000400000000000004540',
+ 'MULTILINESTRING ZM ((10 10 5 45,20 20 4 44),(15 15 3 43,30 15 2 42))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000050000000200c0000002000000024024000000000000402400000000000040140000000000004046800000000000403400000000000040340000000000004010000000000000404600000000000000c000000200000002402e000000000000402e00000000000040080000000000004045800000000000403e000000000000402e00000000000040000000000000004045000000000000',
+ 'MULTILINESTRING ZM ((10 10 5 45,20 20 4 44),(15 15 3 43,30 15 2 42))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01be0b00000200000001bb0b0000010000000500000000000000000024400000000000002440000000000000144000000000008046400000000000002440000000000000344000000000000010400000000000004640000000000000344000000000000034400000000000000840000000000080454000000000000034400000000000002e400000000000000040000000000000454000000000000024400000000000002440000000000000f03f000000000080444001bb0b000001000000040000000000000000004e400000000000004e4000000000000014400000000000804640000000000080514000000000008051400000000000001040000000000000464000000000000054400000000000004e40000000000000084000000000008045400000000000004e400000000000004e4000000000000000400000000000004540',
+ 'MULTIPOLYGON ZM (((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41)),((60 60 5 45,70 70 4 44,80 60 3 43,60 60 2 42)))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbe000000020000000bbb00000001000000054024000000000000402400000000000040140000000000004046800000000000402400000000000040340000000000004010000000000000404600000000000040340000000000004034000000000000400800000000000040458000000000004034000000000000402e00000000000040000000000000004045000000000000402400000000000040240000000000003ff000000000000040448000000000000000000bbb0000000100000004404e000000000000404e0000000000004014000000000000404680000000000040518000000000004051800000000000401000000000000040460000000000004054000000000000404e00000000000040080000000000004045800000000000404e000000000000404e00000000000040000000000000004045000000000000',
+ 'MULTIPOLYGON ZM (((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41)),((60 60 5 45,70 70 4 44,80 60 3 43,60 60 2 42)))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01060000c00200000001030000c0010000000500000000000000000024400000000000002440000000000000144000000000008046400000000000002440000000000000344000000000000010400000000000004640000000000000344000000000000034400000000000000840000000000080454000000000000034400000000000002e400000000000000040000000000000454000000000000024400000000000002440000000000000f03f000000000080444001030000c001000000040000000000000000004e400000000000004e4000000000000014400000000000804640000000000080514000000000008051400000000000001040000000000000464000000000000054400000000000004e40000000000000084000000000008045400000000000004e400000000000004e4000000000000000400000000000004540',
+ 'MULTIPOLYGON ZM (((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41)),((60 60 5 45,70 70 4 44,80 60 3 43,60 60 2 42)))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000060000000200c000000300000001000000054024000000000000402400000000000040140000000000004046800000000000402400000000000040340000000000004010000000000000404600000000000040340000000000004034000000000000400800000000000040458000000000004034000000000000402e00000000000040000000000000004045000000000000402400000000000040240000000000003ff0000000000000404480000000000000c00000030000000100000004404e000000000000404e0000000000004014000000000000404680000000000040518000000000004051800000000000401000000000000040460000000000004054000000000000404e00000000000040080000000000004045800000000000404e000000000000404e00000000000040000000000000004045000000000000',
+ 'MULTIPOLYGON ZM (((10 10 5 45,10 20 4 44,20 20 3 43,20 15 2 42,10 10 1 41)),((60 60 5 45,70 70 4 44,80 60 3 43,60 60 2 42)))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bf0b00000300000001b90b0000000000000000244000000000000024400000000000001440000000000080464001b90b00000000000000003e400000000000003e400000000000001440000000000080464001ba0b0000020000000000000000002e400000000000002e40000000000000144000000000008046400000000000003440000000000000344000000000000014400000000000804640',
+ 'GEOMETRYCOLLECTION ZM (POINT ZM (10 10 5 45),POINT ZM (30 30 5 45),LINESTRING ZM (15 15 5 45,20 20 5 45))',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbf000000030000000bb940240000000000004024000000000000401400000000000040468000000000000000000bb9403e000000000000403e000000000000401400000000000040468000000000000000000bba00000002402e000000000000402e000000000000401400000000000040468000000000004034000000000000403400000000000040140000000000004046800000000000',
+ 'GEOMETRYCOLLECTION ZM (POINT ZM (10 10 5 45),POINT ZM (30 30 5 45),LINESTRING ZM (15 15 5 45,20 20 5 45))',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01070000c00300000001010000c0000000000000244000000000000024400000000000001440000000000080464001010000c00000000000003e400000000000003e400000000000001440000000000080464001020000c0020000000000000000002e400000000000002e40000000000000144000000000008046400000000000003440000000000000344000000000000014400000000000804640',
+ 'GEOMETRYCOLLECTION ZM (POINT ZM (10 10 5 45),POINT ZM (30 30 5 45),LINESTRING ZM (15 15 5 45,20 20 5 45))',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000070000000300c0000001402400000000000040240000000000004014000000000000404680000000000000c0000001403e000000000000403e0000000000004014000000000000404680000000000000c000000200000002402e000000000000402e000000000000401400000000000040468000000000004034000000000000403400000000000040140000000000004046800000000000',
+ 'GEOMETRYCOLLECTION ZM (POINT ZM (10 10 5 45),POINT ZM (30 30 5 45),LINESTRING ZM (15 15 5 45,20 20 5 45))',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+
+ [
+ '0101000000000000000000f87f000000000000f87f',
+ 'POINT EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '00000000017ff80000000000007ff8000000000000',
+ 'POINT EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010200000000000000',
+ 'LINESTRING EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000200000000',
+ 'LINESTRING EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010300000000000000',
+ 'POLYGON EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000300000000',
+ 'POLYGON EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010400000000000000',
+ 'MULTIPOINT EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000400000000',
+ 'MULTIPOINT EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010500000000000000',
+ 'MULTILINESTRING EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000500000000',
+ 'MULTILINESTRING EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010600000000000000',
+ 'MULTIPOLYGON EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000600000000',
+ 'MULTIPOLYGON EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '010700000000000000',
+ 'GEOMETRYCOLLECTION EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XY'},
+ ],
+ [
+ '000000000700000000',
+ 'GEOMETRYCOLLECTION EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XY'},
+ ],
+
+ /*
+ // XXX: These doesn't work since WKT parser returns 2D geometry instead of 3D/4D
+ ['01e9030000000000000000f87f000000000000f87f000000000000f87f', 'POINT Z EMPTY', { littleEndian: true, ewkb: false, geometryLayout: 'XYZ' }],
+ ['00000003e97ff80000000000007ff80000000000007ff8000000000000', 'POINT Z EMPTY', { littleEndian: false, ewkb: false, geometryLayout: 'XYZ' }],
+ ['0101000080000000000000f87f000000000000f87f000000000000f87f', 'POINT Z EMPTY', { littleEndian: true, ewkb: true, geometryLayout: 'XYZ' }],
+ ['00800000017ff80000000000007ff80000000000007ff8000000000000', 'POINT Z EMPTY', { littleEndian: false, ewkb: true, geometryLayout: 'XYZ' }],
+ */
+ [
+ '01ea03000000000000',
+ 'LINESTRING Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ea00000000',
+ 'LINESTRING Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010200008000000000',
+ 'LINESTRING Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000200000000',
+ 'LINESTRING Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01eb03000000000000',
+ 'POLYGON Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003eb00000000',
+ 'POLYGON Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010300008000000000',
+ 'POLYGON Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000300000000',
+ 'POLYGON Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ec03000000000000',
+ 'MULTIPOINT Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ec00000000',
+ 'MULTIPOINT Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010400008000000000',
+ 'MULTIPOINT Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000400000000',
+ 'MULTIPOINT Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ed03000000000000',
+ 'MULTILINESTRING Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ed00000000',
+ 'MULTILINESTRING Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010500008000000000',
+ 'MULTILINESTRING Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000500000000',
+ 'MULTILINESTRING Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ee03000000000000',
+ 'MULTIPOLYGON Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ee00000000',
+ 'MULTIPOLYGON Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010600008000000000',
+ 'MULTIPOLYGON Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000600000000',
+ 'MULTIPOLYGON Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01ef03000000000000',
+ 'GEOMETRYCOLLECTION Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003ef00000000',
+ 'GEOMETRYCOLLECTION Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '010700008000000000',
+ 'GEOMETRYCOLLECTION Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '008000000700000000',
+ 'GEOMETRYCOLLECTION Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+
+ /*
+ // XXX: These doesn't work since WKT parser returns 2D geometry instead of 3D/4D
+ ['01d1070000000000000000f87f000000000000f87f000000000000f87f', 'POINT M EMPTY', { littleEndian: true, ewkb: false, geometryLayout: 'XYM' }],
+ ['00000007d17ff80000000000007ff80000000000007ff8000000000000', 'POINT M EMPTY', { littleEndian: false, ewkb: false, geometryLayout: 'XYM' }],
+ ['0101000040000000000000f87f000000000000f87f000000000000f87f', 'POINT M EMPTY', { littleEndian: true, ewkb: true, geometryLayout: 'XYM' }],
+ ['00400000017ff80000000000007ff80000000000007ff8000000000000', 'POINT M EMPTY', { littleEndian: false, ewkb: true, geometryLayout: 'XYM' }],
+ */
+ [
+ '01d207000000000000',
+ 'LINESTRING M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d200000000',
+ 'LINESTRING M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010200004000000000',
+ 'LINESTRING M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000200000000',
+ 'LINESTRING M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d307000000000000',
+ 'POLYGON M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d300000000',
+ 'POLYGON M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010300004000000000',
+ 'POLYGON M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000300000000',
+ 'POLYGON M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d407000000000000',
+ 'MULTIPOINT M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d400000000',
+ 'MULTIPOINT M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010400004000000000',
+ 'MULTIPOINT M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000400000000',
+ 'MULTIPOINT M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d507000000000000',
+ 'MULTILINESTRING M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d500000000',
+ 'MULTILINESTRING M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010500004000000000',
+ 'MULTILINESTRING M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000500000000',
+ 'MULTILINESTRING M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d607000000000000',
+ 'MULTIPOLYGON M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d600000000',
+ 'MULTIPOLYGON M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010600004000000000',
+ 'MULTIPOLYGON M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000600000000',
+ 'MULTIPOLYGON M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01d707000000000000',
+ 'GEOMETRYCOLLECTION M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d700000000',
+ 'GEOMETRYCOLLECTION M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '010700004000000000',
+ 'GEOMETRYCOLLECTION M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '004000000700000000',
+ 'GEOMETRYCOLLECTION M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+
+ /*
+ // XXX: These doesn't work since WKT parser returns 2D geometry instead of 3D/4D
+ ['01b90b0000000000000000f87f000000000000f87f000000000000f87f000000000000f87f', 'POINT ZM EMPTY', { littleEndian: true, ewkb: false, geometryLayout: 'XYZM' }],
+ ['0000000bb97ff80000000000007ff80000000000007ff80000000000007ff8000000000000', 'POINT ZM EMPTY', { littleEndian: false, ewkb: false, geometryLayout: 'XYZM' }],
+ ['01010000c0000000000000f87f000000000000f87f000000000000f87f000000000000f87f', 'POINT ZM EMPTY', { littleEndian: true, ewkb: true, geometryLayout: 'XYZM' }],
+ ['00c00000017ff80000000000007ff80000000000007ff80000000000007ff8000000000000', 'POINT ZM EMPTY', { littleEndian: false, ewkb: true, geometryLayout: 'XYZM' }],
+ */
+ [
+ '01ba0b000000000000',
+ 'LINESTRING ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bba00000000',
+ 'LINESTRING ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01020000c000000000',
+ 'LINESTRING ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000200000000',
+ 'LINESTRING ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bb0b000000000000',
+ 'POLYGON ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbb00000000',
+ 'POLYGON ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01030000c000000000',
+ 'POLYGON ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000300000000',
+ 'POLYGON ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bc0b000000000000',
+ 'MULTIPOINT ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbc00000000',
+ 'MULTIPOINT ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01040000c000000000',
+ 'MULTIPOINT ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000400000000',
+ 'MULTIPOINT ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bd0b000000000000',
+ 'MULTILINESTRING ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbd00000000',
+ 'MULTILINESTRING ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01050000c000000000',
+ 'MULTILINESTRING ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000500000000',
+ 'MULTILINESTRING ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01be0b000000000000',
+ 'MULTIPOLYGON ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbe00000000',
+ 'MULTIPOLYGON ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01060000c000000000',
+ 'MULTIPOLYGON ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000600000000',
+ 'MULTIPOLYGON ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01bf0b000000000000',
+ 'GEOMETRYCOLLECTION ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bbf00000000',
+ 'GEOMETRYCOLLECTION ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01070000c000000000',
+ 'GEOMETRYCOLLECTION ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c000000700000000',
+ 'GEOMETRYCOLLECTION ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+];
+
+describe('ol.format.WKB', function () {
+ const format = new WKB();
+
+ describe('#readProjection(string)', function () {
+ it('returns the default projection', function () {
+ const wkb = '0101000000000000000000F03F0000000000000040'; // POINT(1 2)
+ const projection = format.readProjection(wkb);
+ expect(projection).to.be(undefined);
+ });
+
+ it('returns an embed projection', function () {
+ const wkb = '0101000020E6100000000000000000F03F0000000000000040'; // SRID=4326;POINT(1 2)
+ const projection = format.readProjection(wkb);
+ expect(projection.getCode()).to.be('EPSG:4326');
+ });
+ });
+
+ describe('#readProjection(Uint8Array)', function () {
+ it('returns the default projection', function () {
+ const wkb = new Uint8Array([
+ // POINT(1 2)
+ 0x01,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xf0,
+ 0x3f,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ ]);
+ const projection = format.readProjection(wkb);
+ expect(projection).to.be(undefined);
+ });
+
+ it('returns an embed projection', function () {
+ const wkb = new Uint8Array([
+ // SRID=4326;POINT(1 2)
+ 0x01,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x20,
+ 0xe6,
+ 0x10,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xf0,
+ 0x3f,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ ]);
+ const projection = format.readProjection(wkb);
+ expect(projection.getCode()).to.be('EPSG:4326');
+ });
+ });
+
+ describe('#readGeometry(string)', function () {
+ it('transforms with dataProjection and featureProjection', function () {
+ const wkb = '0101000000000000000000F03F0000000000000040'; // POINT(1 2)
+ const geom = format.readGeometry(wkb, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ expect(geom.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+
+ it('transforms with auto detection of dataProjection', function () {
+ const wkb = '0101000020E6100000000000000000F03F0000000000000040'; // SRID=4326;POINT(1 2)
+ const geom = format.readGeometry(wkb, {
+ featureProjection: 'EPSG:3857',
+ });
+ expect(geom.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+ });
+
+ describe('#readGeometry(Uint8Array)', function () {
+ it('transforms with dataProjection and featureProjection', function () {
+ const wkb = new Uint8Array([
+ // POINT(1 2)
+ 0x01,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xf0,
+ 0x3f,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ ]);
+ const geom = format.readGeometry(wkb, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ expect(geom.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+
+ it('transforms with auto detection of dataProjection', function () {
+ const wkb = new Uint8Array([
+ // SRID=4326;POINT(1 2)
+ 0x01,
+ 0x01,
+ 0x00,
+ 0x00,
+ 0x20,
+ 0xe6,
+ 0x10,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0xf0,
+ 0x3f,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x40,
+ ]);
+ const geom = format.readGeometry(wkb, {
+ featureProjection: 'EPSG:3857',
+ });
+ expect(geom.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+ });
+
+ describe('#readGeometry(string)', function () {
+ function compareGeometries(a, b) {
+ expect(a.getType()).to.eql(b.getType());
+
+ if (a instanceof GeometryCollection || b instanceof GeometryCollection) {
+ expect(a).to.be.a(GeometryCollection);
+ expect(b).to.be.a(GeometryCollection);
+
+ const aGeoms = a.getGeometries();
+ const bGeoms = b.getGeometries();
+
+ expect(aGeoms.length).to.eql(bGeoms.length);
+
+ for (let i = 0; i < aGeoms.length; i++) {
+ compareGeometries(aGeoms[i], bGeoms[i]);
+ }
+
+ return;
+ }
+
+ expect(a).to.be.a(SimpleGeometry);
+ expect(b).to.be.a(SimpleGeometry);
+
+ expect(a.getLayout()).to.eql(b.getLayout());
+
+ const aCoords = a.getCoordinates();
+ const bCoords = b.getCoordinates();
+
+ expect(aCoords.length).to.eql(bCoords.length);
+
+ expect(JSON.stringify(aCoords)).to.eql(JSON.stringify(bCoords)); // compare arrays
+ }
+
+ // tests for several patterns
+ for (const pair of patterns) {
+ const wkb = pair[0];
+ const wkt = pair[1];
+
+ it('works on ' + wkb + ' (' + wkt + ')', function () {
+ const wkb_result = new WKB().readGeometry(wkb);
+ const wkt_result = new WKT().readGeometry(wkt);
+
+ compareGeometries(wkb_result, wkt_result);
+ });
+ }
+ });
+
+ describe('#writeGeometry()', function () {
+ it('transforms with dataProjection and featureProjection', function () {
+ const geom = new Point([1, 2]).transform('EPSG:4326', 'EPSG:3857');
+ const wkb = format.writeGeometry(geom, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ const got = format.readGeometry(wkb).getCoordinates();
+ expect(got[0]).to.roughlyEqual(1, 1e-6);
+ expect(got[1]).to.roughlyEqual(2, 1e-6);
+ });
+
+ // tests for several patterns
+ for (const item of patterns) {
+ const wkb = item[0];
+ const wkt = item[1];
+ const opts = item[2];
+
+ it('for ' + wkt + ' with opt ' + JSON.stringify(opts), function () {
+ const wkt_result = new WKT().readGeometry(wkt);
+ const wkb_result = new WKB(opts).writeGeometry(wkt_result);
+
+ expect(wkb_result.toLowerCase()).to.eql(wkb);
+ });
+ }
+
+ // XXX: Additional tests for empty POINT Z/M/ZM since WKT parser returns 2D geometry instead of 3D/4D
+ it('returns proper representation for 3D/4D point', function () {
+ const testPatterns = [
+ [
+ '01e9030000000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT Z EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00000003e97ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT Z EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZ'},
+ ],
+ [
+ '0101000080000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT Z EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '00800000017ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT Z EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZ'},
+ ],
+ [
+ '01d1070000000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT M EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '00000007d17ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT M EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYM'},
+ ],
+ [
+ '0101000040000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT M EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '00400000017ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT M EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYM'},
+ ],
+ [
+ '01b90b0000000000000000f87f000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT ZM EMPTY',
+ {littleEndian: true, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '0000000bb97ff80000000000007ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT ZM EMPTY',
+ {littleEndian: false, ewkb: false, geometryLayout: 'XYZM'},
+ ],
+ [
+ '01010000c0000000000000f87f000000000000f87f000000000000f87f000000000000f87f',
+ 'POINT ZM EMPTY',
+ {littleEndian: true, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ [
+ '00c00000017ff80000000000007ff80000000000007ff80000000000007ff8000000000000',
+ 'POINT ZM EMPTY',
+ {littleEndian: false, ewkb: true, geometryLayout: 'XYZM'},
+ ],
+ ];
+
+ for (const item of testPatterns) {
+ const wkb = item[0];
+ const opts = item[2];
+
+ const geom = new Point([], opts.geometryLayout);
+ expect(new WKB(opts).writeGeometry(geom).toLowerCase()).to.eql(wkb);
+ }
+ });
+
+ it('detects geometry dimension automatically (common case)', function () {
+ const geom = new GeometryCollection([
+ new Point([1, 2, 3], 'XYZ'),
+ new Point([1, 2, 4], 'XYZM'),
+ new Point([1, 2, 3, 4], 'XYZM'),
+ ]);
+ const wkb = new WKB().writeGeometry(geom);
+ const geoms = new WKB().readGeometry(wkb).getGeometries();
+
+ for (let i = 0; i < geoms.length; i++) {
+ expect(geoms[i].getLayout()).to.eql('XYZ');
+ }
+ });
+
+ it('detects geometry dimension automatically (incompatible case)', function () {
+ const geom = new GeometryCollection([
+ new Point([1, 2, 3], 'XYZ'),
+ new Point([1, 2, 4], 'XYM'),
+ new Point([1, 2, 3, 4], 'XYZM'),
+ ]);
+ const wkb = new WKB().writeGeometry(geom);
+ const geoms = new WKB().readGeometry(wkb).getGeometries();
+
+ for (let i = 0; i < geoms.length; i++) {
+ expect(geoms[i].getLayout()).to.eql('XY');
+ }
+ });
+
+ it('interpolates missing Z value', function () {
+ const geom = new GeometryCollection([
+ new Point([1, 2, 3], 'XY'), // 3rd coord is intentional
+ new Point([1, 2, 4], 'XYM'),
+ new Point([1, 2, 3, 4], 'XYZM'),
+ ]);
+ const wkb = new WKB({nodataZ: 98, geometryLayout: 'XYZ'}).writeGeometry(
+ geom
+ );
+
+ // GEOMETRYCOLLECTION Z (POINT Z (1 2 98),POINT Z (1 2 98),POINT Z (1 2 3))
+ expect(wkb).to.eql(
+ '0107000080030000000101000080000000000000F03F000000000000004000000000008058400101000080000000000000F03F000000000000004000000000008058400101000080000000000000F03F00000000000000400000000000000840'
+ );
+ });
+
+ it('interpolates missing M value', function () {
+ const geom = new GeometryCollection([
+ new Point([1, 2, 3], 'XY'), // 3rd coord is intentional
+ new Point([1, 2, 4], 'XYM'),
+ new Point([1, 2, 3, 4], 'XYZM'),
+ ]);
+ const wkb = new WKB({nodataM: 99, geometryLayout: 'XYM'}).writeGeometry(
+ geom
+ );
+
+ // GEOMETRYCOLLECTION M (POINT M (1 2 99),POINT M (1 2 4),POINT M (1 2 4))
+ expect(wkb).to.eql(
+ '0107000040030000000101000040000000000000F03F00000000000000400000000000C058400101000040000000000000F03F000000000000004000000000000010400101000040000000000000F03F00000000000000400000000000001040'
+ );
+ });
+
+ it('interpolates missing Z and M value', function () {
+ const geom = new GeometryCollection([
+ new Point([1, 2, 3], 'XY'), // 3rd coord is intentional
+ new Point([1, 2, 4], 'XYM'),
+ new Point([1, 2, 3, 4], 'XYZM'),
+ ]);
+ const wkb = new WKB({
+ nodataZ: 98,
+ nodataM: 99,
+ geometryLayout: 'XYZM',
+ }).writeGeometry(geom);
+
+ // GEOMETRYCOLLECTION ZM (POINT ZM (1 2 98 99),POINT ZM (1 2 98 4),POINT ZM (1 2 3 4))
+ expect(wkb).to.eql(
+ '01070000C00300000001010000C0000000000000F03F000000000000004000000000008058400000000000C0584001010000C0000000000000F03F00000000000000400000000000805840000000000000104001010000C0000000000000F03F000000000000004000000000000008400000000000001040'
+ );
+ });
+ });
+
+ describe('#readFeature()', function () {
+ it('transforms with dataProjection and featureProjection', function () {
+ const wkb = '0101000000000000000000F03F0000000000000040'; // POINT(1 2)
+ const feature = format.readFeature(wkb, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ const geom = feature.getGeometry();
+ expect(geom.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+ });
+
+ describe('#writeFeature()', function () {
+ it('transforms with dataProjection and featureProjection', function () {
+ const feature = new Feature(
+ new Point([1, 2]).transform('EPSG:4326', 'EPSG:3857')
+ );
+ const wkt = format.writeFeature(feature, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ const gotFeature = format.readFeature(wkt);
+ expect(gotFeature).to.be.a(Feature);
+ const got = gotFeature.getGeometry().getCoordinates();
+ expect(got[0]).to.roughlyEqual(1, 1e-6);
+ expect(got[1]).to.roughlyEqual(2, 1e-6);
+ });
+ });
+
+ describe('#readFeatures()', function () {
+ const format = new WKB({splitCollection: true});
+
+ it('transforms with dataProjection and featureProjection', function () {
+ const wkb =
+ '0107000000020000000101000000000000000000F03F0000000000000040010100000000000000000010400000000000001440'; // GEOMETRYCOLLECTION(POINT(1 2),POINT(4 5))
+ const features = format.readFeatures(wkb, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ expect(features.length).to.eql(2);
+ const point1 = features[0].getGeometry();
+ const point2 = features[1].getGeometry();
+ expect(point1.getType()).to.eql('Point');
+ expect(point2.getType()).to.eql('Point');
+ expect(point1.getCoordinates()).to.eql(
+ transform([1, 2], 'EPSG:4326', 'EPSG:3857')
+ );
+ expect(point2.getCoordinates()).to.eql(
+ transform([4, 5], 'EPSG:4326', 'EPSG:3857')
+ );
+ });
+ });
+
+ describe('#writeFeatures()', function () {
+ const format = new WKB({splitCollection: true});
+
+ it('transforms with dataProjection and featureProjection', function () {
+ const features = [
+ new Feature(new Point([1, 2]).transform('EPSG:4326', 'EPSG:3857')),
+ new Feature(new Point([4, 5]).transform('EPSG:4326', 'EPSG:3857')),
+ ];
+ const wkt = format.writeFeatures(features, {
+ dataProjection: 'EPSG:4326',
+ featureProjection: 'EPSG:3857',
+ });
+ const gotFeatures = format.readFeatures(wkt);
+ expect(gotFeatures).to.have.length(2);
+ expect(gotFeatures[0].getGeometry().getCoordinates()[0]).to.roughlyEqual(
+ 1,
+ 1e-6
+ );
+ expect(gotFeatures[0].getGeometry().getCoordinates()[1]).to.roughlyEqual(
+ 2,
+ 1e-6
+ );
+ expect(gotFeatures[1].getGeometry().getCoordinates()[0]).to.roughlyEqual(
+ 4,
+ 1e-6
+ );
+ expect(gotFeatures[1].getGeometry().getCoordinates()[1]).to.roughlyEqual(
+ 5,
+ 1e-6
+ );
+ });
+ });
+});