UNPKG

@kibeo/loaders.gl-mvt

Version:

Loader for Mapbox Vector Tiles

221 lines (183 loc) 5.29 kB
import { getPolygonSignedArea } from '@math.gl/polygon'; let endPos, cmd, cmdLen, length, x, y, i; export const TEST_EXPORTS = { classifyRings }; export default class VectorTileFeature { static get types() { return ['Unknown', 'Point', 'LineString', 'Polygon']; } constructor(pbf, end, extent, keys, values, firstPassData) { this.properties = {}; this.extent = extent; this.type = 0; this.id = null; this._pbf = pbf; this._geometry = -1; this._keys = keys; this._values = values; this._firstPassData = firstPassData; pbf.readFields(readFeature, this, end); } loadGeometry() { const pbf = this._pbf; pbf.pos = this._geometry; endPos = pbf.readVarint() + pbf.pos; cmd = 1; length = 0; x = 0; y = 0; i = 0; const lines = []; const data = []; while (pbf.pos < endPos) { if (length <= 0) { cmdLen = pbf.readVarint(); cmd = cmdLen & 0x7; length = cmdLen >> 3; } length--; if (cmd === 1 || cmd === 2) { x += pbf.readSVarint(); y += pbf.readSVarint(); if (cmd === 1) { lines.push(i); } data.push(x, y); i += 2; } else if (cmd === 7) { if (i > 0) { const start = lines[lines.length - 1]; data.push(data[start], data[start + 1]); i += 2; } } else { throw new Error("unknown command ".concat(cmd)); } } return { data, lines }; } _toBinaryCoordinates(transform) { let geom = this.loadGeometry(); transform(geom.data, this); const coordLength = 2; switch (this.type) { case 1: this._firstPassData.pointFeaturesCount++; this._firstPassData.pointPositionsCount += geom.lines.length; break; case 2: this._firstPassData.lineFeaturesCount++; this._firstPassData.linePathsCount += geom.lines.length; this._firstPassData.linePositionsCount += geom.data.length / coordLength; break; case 3: const classified = classifyRings(geom); this._firstPassData.polygonFeaturesCount++; this._firstPassData.polygonObjectsCount += classified.lines.length; for (const lines of classified.lines) { this._firstPassData.polygonRingsCount += lines.length; } this._firstPassData.polygonPositionsCount += classified.data.length / coordLength; geom = classified; break; } geom.type = VectorTileFeature.types[this.type]; if (geom.lines.length > 1) { geom.type = "Multi".concat(geom.type); } const result = { type: 'Feature', geometry: geom, properties: this.properties }; if (this.id !== null) { result.id = this.id; } return result; } toBinaryCoordinates(options) { if (typeof options === 'function') { return this._toBinaryCoordinates(options); } const { x, y, z } = options; const size = this.extent * Math.pow(2, z); const x0 = this.extent * x; const y0 = this.extent * y; function project(data) { for (let j = 0, jl = data.length; j < jl; j += 2) { data[j] = (data[j] + x0) * 360 / size - 180; const y2 = 180 - (data[j + 1] + y0) * 360 / size; data[j + 1] = 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90; } } return this._toBinaryCoordinates(project); } } function classifyRings(geom) { const len = geom.lines.length; if (len <= 1) { return { data: geom.data, areas: [[getPolygonSignedArea(geom.data)]], lines: [geom.lines] }; } const areas = []; const polygons = []; let ringAreas; let polygon; let ccw; let offset = 0; for (let i = 0, startIndex, endIndex; i < len; i++) { startIndex = geom.lines[i] - offset; endIndex = geom.lines[i + 1] - offset || geom.data.length; const shape = geom.data.slice(startIndex, endIndex); const area = getPolygonSignedArea(shape); if (area === 0) { const before = geom.data.slice(0, startIndex); const after = geom.data.slice(endIndex); geom.data = before.concat(after); offset += endIndex - startIndex; continue; } if (ccw === undefined) ccw = area < 0; if (ccw === area < 0) { if (polygon) { areas.push(ringAreas); polygons.push(polygon); } polygon = [startIndex]; ringAreas = [area]; } else { ringAreas.push(area); polygon.push(startIndex); } } if (ringAreas) areas.push(ringAreas); if (polygon) polygons.push(polygon); return { areas, lines: polygons, data: geom.data }; } function readFeature(tag, feature, pbf) { if (tag === 1) feature.id = pbf.readVarint();else if (tag === 2) readTag(pbf, feature);else if (tag === 3) feature.type = pbf.readVarint();else if (tag === 4) feature._geometry = pbf.pos; } function readTag(pbf, feature) { const end = pbf.readVarint() + pbf.pos; while (pbf.pos < end) { const key = feature._keys[pbf.readVarint()]; const value = feature._values[pbf.readVarint()]; feature.properties[key] = value; } } //# sourceMappingURL=vector-tile-feature.js.map