Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,9 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 20

- name: Install dependencies
run: npm ci

- name: Run tests
run: npm test
node-version: 24
- run: npm ci
- run: npm test
136 changes: 59 additions & 77 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,26 @@ export class VectorTileFeature {
/** @private */
this._values = values;

pbf.readFields(readFeature, this, end);
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 8) this.id = pbf.readVarint();
else if (tag === 18) {
const tagsEnd = pbf.readVarint() + pbf.pos;
while (pbf.pos < tagsEnd) {
const key = keys[pbf.readVarint()];
const value = values[pbf.readVarint()];
this.properties[key] = value;
}
} else if (tag === 24) this.type = /** @type {0 | 1 | 2 | 3} */ (pbf.readVarint());
else if (tag === 34) {
this._geometry = pbf.pos;
pbf.skip(tag);
} else pbf.skip(tag);
}
}

loadGeometry() {
if (this._geometry < 0) throw new Error('feature has no geometry');
const pbf = this._pbf;
pbf.pos = this._geometry;

Expand All @@ -59,19 +75,20 @@ export class VectorTileFeature {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
if (length === 0) continue;
}

length--;

if (cmd === 1 || cmd === 2) {
if (cmd === 1) { // moveTo
x += pbf.readSVarint();
y += pbf.readSVarint();
if (line) lines.push(line);
line = [new Point(x, y)];

if (cmd === 1) { // moveTo
if (line) lines.push(line);
line = [];
}

} else if (cmd === 2) { // lineTo
x += pbf.readSVarint();
y += pbf.readSVarint();
if (line) line.push(new Point(x, y));

} else if (cmd === 7) {
Expand All @@ -92,6 +109,7 @@ export class VectorTileFeature {
}

bbox() {
if (this._geometry < 0) throw new Error('feature has no geometry');
const pbf = this._pbf;
pbf.pos = this._geometry;

Expand All @@ -110,6 +128,7 @@ export class VectorTileFeature {
const cmdLen = pbf.readVarint();
cmd = cmdLen & 0x7;
length = cmdLen >> 3;
if (length === 0) continue;
}

length--;
Expand Down Expand Up @@ -207,35 +226,6 @@ export class VectorTileFeature {
/** @type {['Unknown', 'Point', 'LineString', 'Polygon']} */
VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon'];

/**
* @param {number} tag
* @param {VectorTileFeature} feature
* @param {Pbf} pbf
*/
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 = /** @type {0 | 1 | 2 | 3} */ (pbf.readVarint());
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 4) feature._geometry = pbf.pos;
}

/**
* @param {Pbf} pbf
* @param {VectorTileFeature} feature
*/
function readTag(pbf, feature) {
const end = pbf.readVarint() + pbf.pos;

while (pbf.pos < end) {
// @ts-expect-error TS2341 deliberately accessing a private property
const key = feature._keys[pbf.readVarint()];
// @ts-expect-error TS2341 deliberately accessing a private property
const value = feature._values[pbf.readVarint()];
feature.properties[key] = value;
}
}

/** classifies an array of rings into polygons with outer rings and holes
* @param {Point[][]} rings
*/
Expand Down Expand Up @@ -304,7 +294,19 @@ export class VectorTileLayer {
* @type {number[]} */
this._features = [];

pbf.readFields(readLayer, this, end);
if (end === undefined) end = pbf.length;
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 10) this.name = pbf.readString();
else if (tag === 18) {
this._features.push(pbf.pos);
pbf.skip(tag);
} else if (tag === 26) this._keys.push(pbf.readString());
else if (tag === 34) this._values.push(readValueMessage(pbf));
else if (tag === 40) this.extent = pbf.readVarint();
else if (tag === 120) this.version = pbf.readVarint();
else pbf.skip(tag);
}

this.length = this._features.length;
}
Expand All @@ -322,23 +324,6 @@ export class VectorTileLayer {
}
}

/**
* @param {number} tag
* @param {VectorTileLayer} layer
* @param {Pbf} pbf
*/
function readLayer(tag, layer, pbf) {
if (tag === 15) layer.version = pbf.readVarint();
else if (tag === 1) layer.name = pbf.readString();
else if (tag === 5) layer.extent = pbf.readVarint();
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 2) layer._features.push(pbf.pos);
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 3) layer._keys.push(pbf.readString());
// @ts-expect-error TS2341 deliberately accessing a private property
else if (tag === 4) layer._values.push(readValueMessage(pbf));
}

/**
* @param {Pbf} pbf
*/
Expand All @@ -347,15 +332,16 @@ function readValueMessage(pbf) {
const end = pbf.readVarint() + pbf.pos;

while (pbf.pos < end) {
const tag = pbf.readVarint() >> 3;

value = tag === 1 ? pbf.readString() :
tag === 2 ? pbf.readFloat() :
tag === 3 ? pbf.readDouble() :
tag === 4 ? pbf.readVarint64() :
tag === 5 ? pbf.readVarint() :
tag === 6 ? pbf.readSVarint() :
tag === 7 ? pbf.readBoolean() : null;
const tag = pbf.readVarint();
value =
tag === 10 ? pbf.readString() :
tag === 21 ? pbf.readFloat() :
tag === 25 ? pbf.readDouble() :
tag === 32 ? pbf.readVarint64() :
tag === 40 ? pbf.readVarint() :
tag === 48 ? pbf.readSVarint() :
tag === 56 ? pbf.readBoolean() :
(pbf.skip(tag), null);
}
if (value == null) {
throw new Error('unknown feature value');
Expand All @@ -369,20 +355,16 @@ export class VectorTile {
* @param {Pbf} pbf
* @param {number} [end]
*/
constructor(pbf, end) {
constructor(pbf, end = pbf.length) {
/** @type {Record<string, VectorTileLayer>} */
this.layers = pbf.readFields(readTile, Object.create(null), end);
}
}

/**
* @param {number} tag
* @param {Record<string, VectorTileLayer>} layers
* @param {Pbf} pbf
*/
function readTile(tag, layers, pbf) {
if (tag === 3) {
const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
if (layer.length) layers[layer.name] = layer;
const layers = Object.create(null);
while (pbf.pos < end) {
const tag = pbf.readVarint();
if (tag === 26) {
const layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos);
if (layer.length) layers[layer.name] = layer;
} else pbf.skip(tag);
}
this.layers = layers;
}
}
Loading