智慧水务管理系统 - 精河县供水工程综合管理平台

decodeMVT.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. // @ts-check
  2. import RuntimeError from "../Core/RuntimeError.js";
  3. // Mapbox Vector Tile specification:
  4. // https://github.com/mapbox/vector-tile-spec/tree/master/2.1
  5. /**
  6. * @typedef {object} MVTPoint
  7. * @property {number} x Tile-local x (0–extent)
  8. * @property {number} y Tile-local y (0–extent)
  9. * @ignore
  10. */
  11. /**
  12. * @typedef {object} MVTFeature
  13. * @property {"Point"|"LineString"|"Polygon"|"Unknown"} type
  14. * @property {Array<MVTPoint>|Array<Array<MVTPoint>>} geometry
  15. * @property {Record<string, string|number|boolean|bigint>} properties
  16. * @ignore
  17. */
  18. /**
  19. * @typedef {object} MVTLayer
  20. * @property {string} name
  21. * @property {number} extent
  22. * @property {MVTFeature[]} features
  23. * @ignore
  24. */
  25. /**
  26. * @typedef {object} DecodedMVT
  27. * @property {MVTLayer[]} layers
  28. * @ignore
  29. */
  30. /**
  31. * @typedef {(string|number|boolean|bigint)} MVTValue
  32. * @ignore
  33. */
  34. /**
  35. * @typedef {object} ReadTagResult
  36. * @property {number} fieldNumber
  37. * @property {number} wireType
  38. * @property {number} newPos
  39. * @ignore
  40. */
  41. /**
  42. * @typedef {object} ReadVarintResult
  43. * @property {number} value
  44. * @property {number} newPos
  45. * @ignore
  46. */
  47. /**
  48. * @typedef {object} ReadBigVarintResult
  49. * @property {bigint} value
  50. * @property {number} newPos
  51. * @ignore
  52. */
  53. const textDecoder = new TextDecoder();
  54. // Geometry type enum from the MVT spec
  55. const GeomType = {
  56. UNKNOWN: 0,
  57. POINT: 1,
  58. LINESTRING: 2,
  59. POLYGON: 3,
  60. };
  61. // Tile message field numbers (spec §4.1)
  62. const TileField = {
  63. LAYERS: 3,
  64. };
  65. // Layer message field numbers (spec §4.1)
  66. const LayerField = {
  67. NAME: 1,
  68. FEATURES: 2,
  69. KEYS: 3,
  70. VALUES: 4,
  71. EXTENT: 5,
  72. };
  73. // Feature message field numbers (spec §4.2)
  74. const FeatureField = {
  75. TAGS: 2,
  76. TYPE: 3,
  77. GEOMETRY: 4,
  78. };
  79. // Value message field numbers (spec §4.4)
  80. const ValueField = {
  81. STRING: 1,
  82. FLOAT: 2,
  83. DOUBLE: 3,
  84. INT64: 4,
  85. UINT64: 5,
  86. SINT64: 6,
  87. BOOL: 7,
  88. };
  89. const geomTypeName = ["Unknown", "Point", "LineString", "Polygon"];
  90. /**
  91. * Decode a Mapbox Vector Tile (MVT / .pbf) binary buffer into layers and
  92. * features. Geometry coordinates remain in tile-local integer space
  93. * (0 – layer.extent, typically 4096).
  94. *
  95. * @param {ArrayBuffer} arrayBuffer The raw .pbf tile binary
  96. * @returns {DecodedMVT}
  97. * @ignore
  98. */
  99. function decodeMVT(arrayBuffer) {
  100. const bytes = new Uint8Array(arrayBuffer);
  101. const layers = [];
  102. let pos = 0;
  103. while (pos < bytes.length) {
  104. const tag = readTag(bytes, pos, bytes.length);
  105. const fieldNumber = tag.fieldNumber;
  106. const wireType = tag.wireType;
  107. pos = tag.newPos;
  108. // Tile.layers = field 3, wire type 2 (length-delimited)
  109. if (fieldNumber === TileField.LAYERS && wireType === 2) {
  110. const layerLength = readVarintLength(bytes, pos, bytes.length);
  111. pos = layerLength.newPos;
  112. const layerEnd = advanceByLength(
  113. pos,
  114. layerLength.value,
  115. bytes.length,
  116. "layer",
  117. );
  118. layers.push(decodeLayer(bytes, pos, layerEnd));
  119. pos = layerEnd;
  120. } else {
  121. pos = skipField(bytes, pos, wireType, bytes.length);
  122. }
  123. }
  124. return { layers };
  125. }
  126. /**
  127. * @param {Uint8Array} bytes
  128. * @param {number} start
  129. * @param {number} end
  130. * @returns {MVTLayer}
  131. * @ignore
  132. */
  133. function decodeLayer(bytes, start, end) {
  134. let pos = start;
  135. let name = "";
  136. let extent = 4096;
  137. /** @type {string[]} */
  138. const keys = [];
  139. /** @type {Array.<string|number|boolean|bigint|undefined>} */
  140. const values = [];
  141. const rawFeatures = [];
  142. while (pos < end) {
  143. const tag = readTag(bytes, pos, end);
  144. const fieldNumber = tag.fieldNumber;
  145. const wireType = tag.wireType;
  146. pos = tag.newPos;
  147. if (fieldNumber === LayerField.NAME && wireType === 2) {
  148. // name
  149. const length = readVarintLength(bytes, pos, end);
  150. pos = length.newPos;
  151. const stringEnd = advanceByLength(pos, length.value, end, "layer name");
  152. name = readString(bytes, pos, length.value);
  153. pos = stringEnd;
  154. } else if (fieldNumber === LayerField.FEATURES && wireType === 2) {
  155. // feature
  156. const length = readVarintLength(bytes, pos, end);
  157. pos = length.newPos;
  158. const featureEnd = advanceByLength(pos, length.value, end, "feature");
  159. rawFeatures.push({ start: pos, end: featureEnd });
  160. pos = featureEnd;
  161. } else if (fieldNumber === LayerField.KEYS && wireType === 2) {
  162. // key
  163. const length = readVarintLength(bytes, pos, end);
  164. pos = length.newPos;
  165. const stringEnd = advanceByLength(pos, length.value, end, "key");
  166. keys.push(readString(bytes, pos, length.value));
  167. pos = stringEnd;
  168. } else if (fieldNumber === LayerField.VALUES && wireType === 2) {
  169. // value
  170. const length = readVarintLength(bytes, pos, end);
  171. pos = length.newPos;
  172. const valueEnd = advanceByLength(pos, length.value, end, "value");
  173. const value = decodeValue(bytes, pos, valueEnd);
  174. values.push(value);
  175. pos = valueEnd;
  176. } else if (fieldNumber === LayerField.EXTENT && wireType === 0) {
  177. // extent
  178. const value = readVarint32(bytes, pos, end);
  179. extent = value.value;
  180. pos = value.newPos;
  181. } else {
  182. pos = skipField(bytes, pos, wireType, end);
  183. }
  184. }
  185. const features = rawFeatures.map(({ start: featureStart, end: featureEnd }) =>
  186. decodeFeature(bytes, featureStart, featureEnd, keys, values),
  187. );
  188. return { name, extent, features };
  189. }
  190. /**
  191. * @param {Uint8Array} bytes
  192. * @param {number} start
  193. * @param {number} end
  194. * @param {string[]} keys
  195. * @param {Array.<string|number|boolean|bigint|undefined>} values
  196. * @returns {MVTFeature}
  197. * @ignore
  198. */
  199. function decodeFeature(bytes, start, end, keys, values) {
  200. let pos = start;
  201. let geomType = GeomType.UNKNOWN;
  202. const tags = [];
  203. const geometryCommands = [];
  204. while (pos < end) {
  205. const tag = readTag(bytes, pos, end);
  206. const fieldNumber = tag.fieldNumber;
  207. const wireType = tag.wireType;
  208. pos = tag.newPos;
  209. if (fieldNumber === FeatureField.TYPE && wireType === 0) {
  210. // geometry type
  211. const value = readVarint32(bytes, pos, end);
  212. geomType = value.value;
  213. pos = value.newPos;
  214. } else if (fieldNumber === FeatureField.TAGS && wireType === 2) {
  215. // tags (packed uint32)
  216. const length = readVarintLength(bytes, pos, end);
  217. pos = length.newPos;
  218. const tagEnd = advanceByLength(pos, length.value, end, "feature tags");
  219. while (pos < tagEnd) {
  220. const value = readVarint32(bytes, pos, tagEnd);
  221. tags.push(value.value);
  222. pos = value.newPos;
  223. }
  224. } else if (fieldNumber === FeatureField.GEOMETRY && wireType === 2) {
  225. // geometry (packed uint32 commands)
  226. const length = readVarintLength(bytes, pos, end);
  227. pos = length.newPos;
  228. const geometryEnd = advanceByLength(
  229. pos,
  230. length.value,
  231. end,
  232. "feature geometry",
  233. );
  234. while (pos < geometryEnd) {
  235. const value = readVarint32(bytes, pos, geometryEnd);
  236. geometryCommands.push(value.value);
  237. pos = value.newPos;
  238. }
  239. } else {
  240. pos = skipField(bytes, pos, wireType, end);
  241. }
  242. }
  243. // Build properties from tags
  244. /** @type {Record<string, string|number|boolean|bigint>} */
  245. const properties = {};
  246. for (let i = 0; i < tags.length - 1; i += 2) {
  247. const key = keys[tags[i]];
  248. const value = values[tags[i + 1]];
  249. if (typeof key !== "string" || value === undefined) {
  250. continue;
  251. }
  252. properties[key] = value;
  253. }
  254. const geometry = decodeGeometry(geomType, geometryCommands);
  255. return {
  256. type: /** @type {"Point"|"LineString"|"Polygon"|"Unknown"} */ (
  257. geomTypeName[geomType] ?? "Unknown"
  258. ),
  259. geometry,
  260. properties,
  261. };
  262. }
  263. /**
  264. * Decode MVT geometry commands into coordinate arrays.
  265. * @param {number} geomType
  266. * @param {number[]} cmds
  267. * @returns {*}
  268. * @ignore
  269. */
  270. function decodeGeometry(geomType, cmds) {
  271. let i = 0;
  272. let x = 0;
  273. let y = 0;
  274. if (geomType === GeomType.POINT) {
  275. const points = [];
  276. while (i < cmds.length) {
  277. const cmd = cmds[i++];
  278. const cmdId = cmd & 0x7;
  279. const count = cmd >> 3;
  280. if (cmdId === 1) {
  281. // MoveTo
  282. for (let c = 0; c < count; c++) {
  283. x += zigzag(cmds[i++]);
  284. y += zigzag(cmds[i++]);
  285. points.push({ x, y });
  286. }
  287. }
  288. }
  289. return points;
  290. }
  291. if (geomType === GeomType.LINESTRING) {
  292. const lines = [];
  293. let current = null;
  294. while (i < cmds.length) {
  295. const cmd = cmds[i++];
  296. const cmdId = cmd & 0x7;
  297. const count = cmd >> 3;
  298. if (cmdId === 1) {
  299. // MoveTo - start new line
  300. if (current !== null) {
  301. lines.push(current);
  302. }
  303. current = [];
  304. x += zigzag(cmds[i++]);
  305. y += zigzag(cmds[i++]);
  306. current.push({ x, y });
  307. } else if (cmdId === 2) {
  308. // LineTo
  309. for (let c = 0; c < count; c++) {
  310. x += zigzag(cmds[i++]);
  311. y += zigzag(cmds[i++]);
  312. current.push({ x, y });
  313. }
  314. }
  315. }
  316. if (current !== null) {
  317. lines.push(current);
  318. }
  319. return lines;
  320. }
  321. if (geomType === GeomType.POLYGON) {
  322. const rings = [];
  323. let current = null;
  324. while (i < cmds.length) {
  325. const cmd = cmds[i++];
  326. const cmdId = cmd & 0x7;
  327. const count = cmd >> 3;
  328. if (cmdId === 1) {
  329. // MoveTo - start ring
  330. if (current !== null) {
  331. rings.push(current);
  332. }
  333. current = [];
  334. x += zigzag(cmds[i++]);
  335. y += zigzag(cmds[i++]);
  336. current.push({ x, y });
  337. } else if (cmdId === 2) {
  338. // LineTo
  339. for (let c = 0; c < count; c++) {
  340. x += zigzag(cmds[i++]);
  341. y += zigzag(cmds[i++]);
  342. current.push({ x, y });
  343. }
  344. } else if (cmdId === 7) {
  345. // ClosePath
  346. if (current !== null && current.length > 0) {
  347. current.push({ x: current[0].x, y: current[0].y });
  348. rings.push(current);
  349. current = null;
  350. }
  351. }
  352. }
  353. if (current !== null) {
  354. rings.push(current);
  355. }
  356. return rings;
  357. }
  358. return [];
  359. }
  360. /**
  361. * @param {Uint8Array} bytes
  362. * @param {number} start
  363. * @param {number} end
  364. * @returns {string|number|boolean|bigint|undefined}
  365. * @ignore
  366. */
  367. function decodeValue(bytes, start, end) {
  368. let pos = start;
  369. while (pos < end) {
  370. const tag = readTag(bytes, pos, end);
  371. const fieldNumber = tag.fieldNumber;
  372. const wireType = tag.wireType;
  373. pos = tag.newPos;
  374. if (fieldNumber === ValueField.STRING && wireType === 2) {
  375. const length = readVarintLength(bytes, pos, end);
  376. pos = length.newPos;
  377. const stringEnd = advanceByLength(pos, length.value, end, "string value");
  378. return readString(bytes, pos, stringEnd - pos);
  379. } else if (fieldNumber === ValueField.FLOAT && wireType === 5) {
  380. // float
  381. advanceByLength(pos, 4, end, "float value");
  382. const v = new DataView(
  383. bytes.buffer,
  384. bytes.byteOffset + pos,
  385. 4,
  386. ).getFloat32(0, true);
  387. return v;
  388. } else if (fieldNumber === ValueField.DOUBLE && wireType === 1) {
  389. // double
  390. advanceByLength(pos, 8, end, "double value");
  391. const v = new DataView(
  392. bytes.buffer,
  393. bytes.byteOffset + pos,
  394. 8,
  395. ).getFloat64(0, true);
  396. return v;
  397. } else if (fieldNumber === ValueField.INT64 && wireType === 0) {
  398. const value = readBigVarint(bytes, pos, end);
  399. return toSafeNumber(value.value);
  400. } else if (fieldNumber === ValueField.UINT64 && wireType === 0) {
  401. const value = readBigVarint(bytes, pos, end);
  402. return toSafeNumber(value.value);
  403. } else if (fieldNumber === ValueField.SINT64 && wireType === 0) {
  404. const value = readBigVarint(bytes, pos, end);
  405. return toSafeNumber(zigzagBigInt(value.value));
  406. } else if (fieldNumber === ValueField.BOOL && wireType === 0) {
  407. const value = readVarint32(bytes, pos, end);
  408. return value.value !== 0;
  409. }
  410. pos = skipField(bytes, pos, wireType, end);
  411. }
  412. return undefined;
  413. }
  414. /**
  415. * @param {Uint8Array} bytes
  416. * @param {number} pos
  417. * @param {number} limit
  418. * @returns {ReadTagResult}
  419. * @ignore
  420. */
  421. function readTag(bytes, pos, limit) {
  422. const value = readVarint32(bytes, pos, limit);
  423. return {
  424. fieldNumber: value.value >>> 3,
  425. wireType: value.value & 0x7,
  426. newPos: value.newPos,
  427. };
  428. }
  429. /**
  430. * @param {Uint8Array} bytes
  431. * @param {number} pos
  432. * @param {number} limit
  433. * @returns {ReadVarintResult}
  434. * @ignore
  435. */
  436. function readVarint32(bytes, pos, limit) {
  437. const value = readBigVarint(bytes, pos, limit, 5);
  438. if (value.value > 0xffffffffn) {
  439. throw new RuntimeError("Invalid MVT uint32 varint.");
  440. }
  441. return {
  442. value: Number(value.value),
  443. newPos: value.newPos,
  444. };
  445. }
  446. /**
  447. * @param {Uint8Array} bytes
  448. * @param {number} pos
  449. * @param {number} limit
  450. * @returns {ReadVarintResult}
  451. * @ignore
  452. */
  453. function readVarintLength(bytes, pos, limit) {
  454. const value = readBigVarint(bytes, pos, limit);
  455. if (value.value > BigInt(Number.MAX_SAFE_INTEGER)) {
  456. throw new RuntimeError("Invalid MVT length varint.");
  457. }
  458. return {
  459. value: Number(value.value),
  460. newPos: value.newPos,
  461. };
  462. }
  463. /**
  464. * @param {Uint8Array} bytes
  465. * @param {number} pos
  466. * @param {number} limit
  467. * @param {number} [maxBytes=10]
  468. * @returns {ReadBigVarintResult}
  469. * @ignore
  470. */
  471. function readBigVarint(bytes, pos, limit, maxBytes) {
  472. let result = 0n;
  473. let shift = 0n;
  474. let byteCount = 0;
  475. const byteLimit = maxBytes ?? 10;
  476. while (true) {
  477. if (pos >= limit || pos >= bytes.length) {
  478. throw new RuntimeError("Invalid MVT: truncated varint.");
  479. }
  480. const byte = bytes[pos++];
  481. result |= BigInt(byte & 0x7f) << shift;
  482. byteCount++;
  483. if ((byte & 0x80) === 0) {
  484. break;
  485. }
  486. if (byteCount >= byteLimit) {
  487. throw new RuntimeError("Invalid MVT: varint is too long.");
  488. }
  489. shift += 7n;
  490. }
  491. return {
  492. value: result,
  493. newPos: pos,
  494. };
  495. }
  496. /**
  497. * @param {Uint8Array} bytes
  498. * @param {number} pos
  499. * @param {number} len
  500. * @returns {string}
  501. * @ignore
  502. */
  503. function readString(bytes, pos, len) {
  504. const end = advanceByLength(pos, len, bytes.length, "string");
  505. return textDecoder.decode(bytes.subarray(pos, end));
  506. }
  507. /**
  508. * @param {number} pos
  509. * @param {number} length
  510. * @param {number} limit
  511. * @param {string} fieldName
  512. * @returns {number}
  513. * @ignore
  514. */
  515. function advanceByLength(pos, length, limit, fieldName) {
  516. if (!Number.isFinite(length) || length < 0) {
  517. throw new RuntimeError(`Invalid MVT ${fieldName}: invalid length.`);
  518. }
  519. const end = pos + length;
  520. if (!Number.isFinite(end) || end < pos || end > limit) {
  521. throw new RuntimeError(
  522. `Invalid MVT ${fieldName}: length exceeds available bytes.`,
  523. );
  524. }
  525. return end;
  526. }
  527. /**
  528. * @param {Uint8Array} bytes
  529. * @param {number} pos
  530. * @param {number} wireType
  531. * @param {number} limit
  532. * @returns {number} newPos
  533. * @ignore
  534. */
  535. function skipField(bytes, pos, wireType, limit) {
  536. if (wireType === 0) {
  537. return readBigVarint(bytes, pos, limit).newPos;
  538. } else if (wireType === 1) {
  539. return advanceByLength(pos, 8, limit, "fixed64 field");
  540. } else if (wireType === 2) {
  541. const length = readVarintLength(bytes, pos, limit);
  542. return advanceByLength(
  543. length.newPos,
  544. length.value,
  545. limit,
  546. "length-delimited field",
  547. );
  548. } else if (wireType === 5) {
  549. return advanceByLength(pos, 4, limit, "fixed32 field");
  550. }
  551. throw new RuntimeError(`Unsupported protobuf wire type: ${wireType}`);
  552. }
  553. /**
  554. * Decode a zigzag-encoded signed integer.
  555. * @param {number} n
  556. * @returns {number}
  557. * @ignore
  558. */
  559. function zigzag(n) {
  560. return (n >>> 1) ^ -(n & 1);
  561. }
  562. /**
  563. * @param {bigint} n
  564. * @returns {bigint}
  565. * @ignore
  566. */
  567. function zigzagBigInt(n) {
  568. return (n >> 1n) ^ -(n & 1n);
  569. }
  570. /**
  571. * @param {bigint} value
  572. * @returns {number|bigint}
  573. * @ignore
  574. */
  575. function toSafeNumber(value) {
  576. if (
  577. value <= BigInt(Number.MAX_SAFE_INTEGER) &&
  578. value >= BigInt(Number.MIN_SAFE_INTEGER)
  579. ) {
  580. return Number(value);
  581. }
  582. return value;
  583. }
  584. export default decodeMVT;