Merge pull request #106 from protomaps/vtfilter

Vector tile filtering
This commit is contained in:
Brandon Liu
2022-12-21 16:45:18 +08:00
committed by GitHub
10 changed files with 1267 additions and 4 deletions

View File

@@ -47,3 +47,4 @@ jobs:
- run: cd python && python -m unittest test/test_* - run: cd python && python -m unittest test/test_*
- run: cd cpp && make - run: cd cpp && make
- run: cd serverless/cloudflare && npm install && npm test - run: cd serverless/cloudflare && npm install && npm test
- run: cd serverless/vtfilter && npm install && npm test

View File

@@ -136,9 +136,10 @@ const apiResp = (
// Assumes event is a API Gateway V2 or Lambda Function URL formatted dict // Assumes event is a API Gateway V2 or Lambda Function URL formatted dict
// and returns API Gateway V2 / Lambda Function dict responses // and returns API Gateway V2 / Lambda Function dict responses
// Does not work with CloudFront events/Lambda@Edge; see README // Does not work with CloudFront events/Lambda@Edge; see README
export const handler = async ( export const handlerRaw = async (
event: APIGatewayProxyEventV2, event: APIGatewayProxyEventV2,
context: Context context: Context,
tilePostprocess?: (a: ArrayBuffer, t: TileType) => ArrayBuffer
): Promise<APIGatewayProxyResult> => { ): Promise<APIGatewayProxyResult> => {
var path; var path;
var is_api_gateway; var is_api_gateway;
@@ -216,10 +217,16 @@ export const handler = async (
break; break;
} }
let data = tile_result.data;
if (tilePostprocess) {
data = tilePostprocess(data, header.tileType);
}
if (is_api_gateway) { if (is_api_gateway) {
// this is wasted work, but we need to force API Gateway to interpret the Lambda response as binary // this is wasted work, but we need to force API Gateway to interpret the Lambda response as binary
// without depending on clients sending matching Accept: headers in the request. // without depending on clients sending matching Accept: headers in the request.
const recompressed_data = zlib.gzipSync(tile_result.data); const recompressed_data = zlib.gzipSync(data);
headers["Content-Encoding"] = "gzip"; headers["Content-Encoding"] = "gzip";
return apiResp( return apiResp(
200, 200,
@@ -231,7 +238,7 @@ export const handler = async (
// returns uncompressed response // returns uncompressed response
return apiResp( return apiResp(
200, 200,
Buffer.from(tile_result.data).toString("base64"), Buffer.from(data).toString("base64"),
true, true,
headers headers
); );
@@ -247,3 +254,10 @@ export const handler = async (
} }
return apiResp(404, "Invalid URL", false, headers); return apiResp(404, "Invalid URL", false, headers);
}; };
export const handler = async (
event: APIGatewayProxyEventV2,
context: Context
): Promise<APIGatewayProxyResult> => {
return handlerRaw(event, context);
};

1
serverless/vtfilter/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
dist

Binary file not shown.

View File

@@ -0,0 +1,21 @@
import {
Context,
APIGatewayProxyResult,
APIGatewayProxyEventV2,
} from "aws-lambda";
import { TileType } from "../../js/index";
import { handlerRaw } from "../aws/src/index";
import { parseAttributes, vtfilter } from "./vtfilter";
export const handler = async (
event: APIGatewayProxyEventV2,
context: Context
): Promise<APIGatewayProxyResult> => {
let attributes = event.queryStringParameters?.attributes;
function tilePostprocess(a: ArrayBuffer, t: TileType) {
return vtfilter(new Uint8Array(a), attributes);
}
return handlerRaw(event, context, tilePostprocess);
};

855
serverless/vtfilter/package-lock.json generated Normal file
View File

@@ -0,0 +1,855 @@
{
"name": "vtfilter",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.109",
"@types/mapbox__vector-tile": "^1.3.0",
"@types/node": "^18.11.10",
"@types/pbf": "^3.0.2",
"esbuild": "^0.15.7",
"esbuild-runner": "^2.2.1",
"typescript": "^4.8.3"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz",
"integrity": "sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"node_modules/@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"dependencies": {
"@mapbox/point-geometry": "~0.1.0"
}
},
"node_modules/@types/aws-lambda": {
"version": "8.10.109",
"resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz",
"integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg==",
"dev": true
},
"node_modules/@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"node_modules/@types/mapbox__point-geometry": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==",
"dev": true
},
"node_modules/@types/mapbox__vector-tile": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==",
"dev": true,
"dependencies": {
"@types/geojson": "*",
"@types/mapbox__point-geometry": "*",
"@types/pbf": "*"
}
},
"node_modules/@types/node": {
"version": "18.11.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==",
"dev": true
},
"node_modules/@types/pbf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz",
"integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==",
"dev": true
},
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"node_modules/esbuild": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz",
"integrity": "sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/linux-loong64": "0.15.7",
"esbuild-android-64": "0.15.7",
"esbuild-android-arm64": "0.15.7",
"esbuild-darwin-64": "0.15.7",
"esbuild-darwin-arm64": "0.15.7",
"esbuild-freebsd-64": "0.15.7",
"esbuild-freebsd-arm64": "0.15.7",
"esbuild-linux-32": "0.15.7",
"esbuild-linux-64": "0.15.7",
"esbuild-linux-arm": "0.15.7",
"esbuild-linux-arm64": "0.15.7",
"esbuild-linux-mips64le": "0.15.7",
"esbuild-linux-ppc64le": "0.15.7",
"esbuild-linux-riscv64": "0.15.7",
"esbuild-linux-s390x": "0.15.7",
"esbuild-netbsd-64": "0.15.7",
"esbuild-openbsd-64": "0.15.7",
"esbuild-sunos-64": "0.15.7",
"esbuild-windows-32": "0.15.7",
"esbuild-windows-64": "0.15.7",
"esbuild-windows-arm64": "0.15.7"
}
},
"node_modules/esbuild-android-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz",
"integrity": "sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz",
"integrity": "sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz",
"integrity": "sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz",
"integrity": "sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz",
"integrity": "sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz",
"integrity": "sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz",
"integrity": "sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz",
"integrity": "sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz",
"integrity": "sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz",
"integrity": "sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz",
"integrity": "sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz",
"integrity": "sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz",
"integrity": "sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz",
"integrity": "sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz",
"integrity": "sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz",
"integrity": "sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-runner": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.1.tgz",
"integrity": "sha512-VP0VfJJZiZ3cKzdOH59ZceDxx/GzBKra7tiGM8MfFMLv6CR1/cpsvtQ3IsJI3pz7HyeYxtbPyecj3fHwR+3XcQ==",
"dev": true,
"dependencies": {
"source-map-support": "0.5.19",
"tslib": "2.3.1"
},
"bin": {
"esr": "bin/esr.js"
},
"peerDependencies": {
"esbuild": "*"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz",
"integrity": "sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz",
"integrity": "sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz",
"integrity": "sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz",
"integrity": "sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/pbf": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
"dependencies": {
"ieee754": "^1.1.12",
"resolve-protobuf-schema": "^2.1.0"
},
"bin": {
"pbf": "bin/pbf"
}
},
"node_modules/protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"dependencies": {
"protocol-buffers-schema": "^3.3.1"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"dependencies": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
"dev": true
},
"node_modules/typescript": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
}
}
},
"dependencies": {
"@esbuild/linux-loong64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.7.tgz",
"integrity": "sha512-IKznSJOsVUuyt7cDzzSZyqBEcZe+7WlBqTVXiF1OXP/4Nm387ToaXZ0fyLwI1iBlI/bzpxVq411QE2/Bt2XWWw==",
"dev": true,
"optional": true
},
"@mapbox/point-geometry": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
"integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="
},
"@mapbox/vector-tile": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
"integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
"requires": {
"@mapbox/point-geometry": "~0.1.0"
}
},
"@types/aws-lambda": {
"version": "8.10.109",
"resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.109.tgz",
"integrity": "sha512-/ME92FneNyXQzrAfcnQQlW1XkCZGPDlpi2ao1MJwecN+6SbeonKeggU8eybv1DfKli90FAVT1MlIZVXfwVuCyg==",
"dev": true
},
"@types/geojson": {
"version": "7946.0.10",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
"dev": true
},
"@types/mapbox__point-geometry": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz",
"integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==",
"dev": true
},
"@types/mapbox__vector-tile": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz",
"integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==",
"dev": true,
"requires": {
"@types/geojson": "*",
"@types/mapbox__point-geometry": "*",
"@types/pbf": "*"
}
},
"@types/node": {
"version": "18.11.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.10.tgz",
"integrity": "sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==",
"dev": true
},
"@types/pbf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz",
"integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==",
"dev": true
},
"buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"dev": true
},
"esbuild": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.7.tgz",
"integrity": "sha512-7V8tzllIbAQV1M4QoE52ImKu8hT/NLGlGXkiDsbEU5PS6K8Mn09ZnYoS+dcmHxOS9CRsV4IRAMdT3I67IyUNXw==",
"dev": true,
"requires": {
"@esbuild/linux-loong64": "0.15.7",
"esbuild-android-64": "0.15.7",
"esbuild-android-arm64": "0.15.7",
"esbuild-darwin-64": "0.15.7",
"esbuild-darwin-arm64": "0.15.7",
"esbuild-freebsd-64": "0.15.7",
"esbuild-freebsd-arm64": "0.15.7",
"esbuild-linux-32": "0.15.7",
"esbuild-linux-64": "0.15.7",
"esbuild-linux-arm": "0.15.7",
"esbuild-linux-arm64": "0.15.7",
"esbuild-linux-mips64le": "0.15.7",
"esbuild-linux-ppc64le": "0.15.7",
"esbuild-linux-riscv64": "0.15.7",
"esbuild-linux-s390x": "0.15.7",
"esbuild-netbsd-64": "0.15.7",
"esbuild-openbsd-64": "0.15.7",
"esbuild-sunos-64": "0.15.7",
"esbuild-windows-32": "0.15.7",
"esbuild-windows-64": "0.15.7",
"esbuild-windows-arm64": "0.15.7"
}
},
"esbuild-android-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.7.tgz",
"integrity": "sha512-p7rCvdsldhxQr3YHxptf1Jcd86dlhvc3EQmQJaZzzuAxefO9PvcI0GLOa5nCWem1AJ8iMRu9w0r5TG8pHmbi9w==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.7.tgz",
"integrity": "sha512-L775l9ynJT7rVqRM5vo+9w5g2ysbOCfsdLV4CWanTZ1k/9Jb3IYlQ06VCI1edhcosTYJRECQFJa3eAvkx72eyQ==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.7.tgz",
"integrity": "sha512-KGPt3r1c9ww009t2xLB6Vk0YyNOXh7hbjZ3EecHoVDxgtbUlYstMPDaReimKe6eOEfyY4hBEEeTvKwPsiH5WZg==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.7.tgz",
"integrity": "sha512-kBIHvtVqbSGajN88lYMnR3aIleH3ABZLLFLxwL2stiuIGAjGlQW741NxVTpUHQXUmPzxi6POqc9npkXa8AcSZQ==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.7.tgz",
"integrity": "sha512-hESZB91qDLV5MEwNxzMxPfbjAhOmtfsr9Wnuci7pY6TtEh4UDuevmGmkUIjX/b+e/k4tcNBMf7SRQ2mdNuK/HQ==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.7.tgz",
"integrity": "sha512-dLFR0ChH5t+b3J8w0fVKGvtwSLWCv7GYT2Y2jFGulF1L5HftQLzVGN+6pi1SivuiVSmTh28FwUhi9PwQicXI6Q==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.7.tgz",
"integrity": "sha512-v3gT/LsONGUZcjbt2swrMjwxo32NJzk+7sAgtxhGx1+ZmOFaTRXBAi1PPfgpeo/J//Un2jIKm/I+qqeo4caJvg==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.7.tgz",
"integrity": "sha512-LxXEfLAKwOVmm1yecpMmWERBshl+Kv5YJ/1KnyAr6HRHFW8cxOEsEfisD3sVl/RvHyW//lhYUVSuy9jGEfIRAQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.7.tgz",
"integrity": "sha512-JKgAHtMR5f75wJTeuNQbyznZZa+pjiUHV7sRZp42UNdyXC6TiUYMW/8z8yIBAr2Fpad8hM1royZKQisqPABPvQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.7.tgz",
"integrity": "sha512-P3cfhudpzWDkglutWgXcT2S7Ft7o2e3YDMrP1n0z2dlbUZghUkKCyaWw0zhp4KxEEzt/E7lmrtRu/pGWnwb9vw==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.7.tgz",
"integrity": "sha512-T7XKuxl0VpeFLCJXub6U+iybiqh0kM/bWOTb4qcPyDDwNVhLUiPcGdG2/0S7F93czUZOKP57YiLV8YQewgLHKw==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.7.tgz",
"integrity": "sha512-6mGuC19WpFN7NYbecMIJjeQgvDb5aMuvyk0PDYBJrqAEMkTwg3Z98kEKuCm6THHRnrgsdr7bp4SruSAxEM4eJw==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.7.tgz",
"integrity": "sha512-uUJsezbswAYo/X7OU/P+PuL/EI9WzxsEQXDekfwpQ23uGiooxqoLFAPmXPcRAt941vjlY9jtITEEikWMBr+F/g==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.7.tgz",
"integrity": "sha512-+tO+xOyTNMc34rXlSxK7aCwJgvQyffqEM5MMdNDEeMU3ss0S6wKvbBOQfgd5jRPblfwJ6b+bKiz0g5nABpY0QQ==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.7.tgz",
"integrity": "sha512-yVc4Wz+Pu3cP5hzm5kIygNPrjar/v5WCSoRmIjCPWfBVJkZNb5brEGKUlf+0Y759D48BCWa0WHrWXaNy0DULTQ==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.7.tgz",
"integrity": "sha512-GsimbwC4FSR4lN3wf8XmTQ+r8/0YSQo21rWDL0XFFhLHKlzEA4SsT1Tl8bPYu00IU6UWSJ+b3fG/8SB69rcuEQ==",
"dev": true,
"optional": true
},
"esbuild-runner": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/esbuild-runner/-/esbuild-runner-2.2.1.tgz",
"integrity": "sha512-VP0VfJJZiZ3cKzdOH59ZceDxx/GzBKra7tiGM8MfFMLv6CR1/cpsvtQ3IsJI3pz7HyeYxtbPyecj3fHwR+3XcQ==",
"dev": true,
"requires": {
"source-map-support": "0.5.19",
"tslib": "2.3.1"
}
},
"esbuild-sunos-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.7.tgz",
"integrity": "sha512-8CDI1aL/ts0mDGbWzjEOGKXnU7p3rDzggHSBtVryQzkSOsjCHRVe0iFYUuhczlxU1R3LN/E7HgUO4NXzGGP/Ag==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.7.tgz",
"integrity": "sha512-cOnKXUEPS8EGCzRSFa1x6NQjGhGsFlVgjhqGEbLTPsA7x4RRYiy2RKoArNUU4iR2vHmzqS5Gr84MEumO/wxYKA==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.7.tgz",
"integrity": "sha512-7MI08Ec2sTIDv+zH6StNBKO+2hGUYIT42GmFyW6MBBWWtJhTcQLinKS6ldIN1d52MXIbiJ6nXyCJ+LpL4jBm3Q==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.15.7",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.7.tgz",
"integrity": "sha512-R06nmqBlWjKHddhRJYlqDd3Fabx9LFdKcjoOy08YLimwmsswlFBJV4rXzZCxz/b7ZJXvrZgj8DDv1ewE9+StMw==",
"dev": true,
"optional": true
},
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"pbf": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
"integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
"requires": {
"ieee754": "^1.1.12",
"resolve-protobuf-schema": "^2.1.0"
}
},
"protocol-buffers-schema": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
"resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
"integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
"requires": {
"protocol-buffers-schema": "^3.3.1"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"source-map-support": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
"dev": true,
"requires": {
"buffer-from": "^1.0.0",
"source-map": "^0.6.0"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
"dev": true
},
"typescript": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
"dev": true
}
}
}

View File

@@ -0,0 +1,22 @@
{
"name": "pmtiles-vtfilter",
"version": "0.0.0",
"dependencies": {
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
},
"devDependencies": {
"@types/aws-lambda": "^8.10.109",
"@types/mapbox__vector-tile": "^1.3.0",
"@types/node": "^18.11.10",
"@types/pbf": "^3.0.2",
"esbuild": "^0.15.7",
"esbuild-runner": "^2.2.1",
"typescript": "^4.8.3"
},
"scripts": {
"test": "node -r esbuild-runner/register vtfilter.test.ts",
"tsc": "tsc --noEmit --watch",
"build": "esbuild index.ts --target=es2020 --outfile=dist/index.mjs --format=esm --bundle --platform=node --target=node18 --external:@aws-sdk/client-s3 --external:@aws-sdk/node-http-handler --banner:js=//$(git describe --always) && cd dist && zip lambda_function.zip index.mjs"
}
}

View File

@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "es2021",
"strict": true,
"preserveConstEnums": true,
"noEmit": true,
"sourceMap": false,
"module": "es2022",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true
},
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,107 @@
import { test } from "node:test";
import assert from "node:assert";
import { vtfilter, parseAttributes } from "./vtfilter";
import fs from "fs";
import { VectorTile } from "@mapbox/vector-tile";
import Protobuf from "pbf";
test("basic attribute filter globals", () => {
let result = parseAttributes("attr1");
assert(result.names.has("attr1"));
assert.strictEqual(result.names.size, 1);
result = parseAttributes("attr1,attr2");
assert(result.names.has("attr1"));
assert(result.names.has("attr2"));
assert.strictEqual(result.names.size, 2);
result = parseAttributes("attr\\:withcolon");
assert(result.names.has("attr:withcolon"));
assert.strictEqual(result.names.size, 1);
result = parseAttributes("attr\\,withcomma");
assert(result.names.has("attr,withcomma"));
assert.strictEqual(result.names.size, 1);
});
test("basic attribute filter by layer", () => {
let result = parseAttributes("layer1:attr1");
assert.strictEqual(result.names.size, 0);
assert.strictEqual(result.byLayer.get("layer1")!.size, 1);
assert(result.byLayer.get("layer1")!.has("attr1"));
result = parseAttributes("attr1,layer1:attr2,layer1:attr3,layer2:attr4");
assert.strictEqual(result.names.size, 1);
assert(result.names.has("attr1"));
assert.strictEqual(result.byLayer.get("layer1")!.size, 2);
assert(result.byLayer.get("layer1")!.has("attr2"));
assert(result.byLayer.get("layer1")!.has("attr3"));
assert.strictEqual(result.byLayer.get("layer2")!.size, 1);
assert(result.byLayer.get("layer2")!.has("attr4"));
result = parseAttributes(
"layer\\,withcomma\\:withcolon:attr\\,withcomma\\:withcolon"
);
assert.strictEqual(result.byLayer.get("layer,withcomma:withcolon")!.size, 1);
assert(
result.byLayer
.get("layer,withcomma:withcolon")!
.has("attr,withcomma:withcolon")
);
});
test("malformed input", () => {
let result = parseAttributes(",");
assert.strictEqual(result.names.size, 0);
result = parseAttributes(":");
assert.strictEqual(result.names.size, 0);
result = parseAttributes("attr:too:manycolons");
assert.strictEqual(result.names.size, 0);
assert.strictEqual(result.byLayer.get("attr")!.size, 1);
assert(result.byLayer.get("attr")!.has("too"));
});
test("1:1", () => {
const data = fs.readFileSync("fixtures/sample.pbf");
let result = vtfilter(data, undefined);
assert(result === data);
result = vtfilter(data, "*");
assert(result === data);
let orig = new VectorTile(new Protobuf(data));
result = vtfilter(data, "*,extra_key_to_make_shaving_run");
let out = new VectorTile(new Protobuf(result));
let orig_values = [];
let out_values = [];
for (let layername in orig.layers) {
assert.strictEqual(
out.layers[layername].length,
orig.layers[layername].length
);
for (var i = 0; i < orig.layers[layername].length; i++) {
let orig_feature = orig.layers[layername].feature(i);
let out_feature = out.layers[layername].feature(i);
orig_values.push(orig_feature.properties);
orig_values.push(orig_feature.loadGeometry());
out_values.push(out_feature.properties);
out_values.push(out_feature.loadGeometry());
}
}
assert.deepEqual(orig_values, out_values);
});
test("filter specific layer", () => {
const data = fs.readFileSync("fixtures/sample.pbf");
const result = vtfilter(data, "water:natural,landuse:*");
let out = new VectorTile(new Protobuf(result));
assert.strictEqual(Object.keys(out.layers).length, 8);
for (var i = 0; i < out.layers["water"].length; i++) {
let feature = out.layers["water"].feature(i);
assert(Object.keys(feature.properties).length < 2);
}
});

View File

@@ -0,0 +1,226 @@
import Protobuf from "pbf";
import {
VectorTile,
VectorTileFeature,
VectorTileLayer,
} from "@mapbox/vector-tile";
interface Attributes {
names: Set<string>;
byLayer: Map<string, Set<string>>;
}
function unescape(s: string): string {
return s.replace("\\,", ",").replace("\\:", ":");
}
export function parseAttributes(s: string): Attributes {
let result: Attributes = {
names: new Set<string>(),
byLayer: new Map<string, Set<string>>(),
};
for (let attr_s of s.split(/(?<!\\),/)) {
let parts = attr_s.split(/(?<!\\):/);
if (parts.length == 1) {
if (parts[0] === "") continue;
result.names.add(unescape(parts[0]));
} else {
let layername = unescape(parts[0]);
let colname = unescape(parts[1]);
if (!result.byLayer.has(layername)) {
result.byLayer.set(layername, new Set<string>());
}
result.byLayer.get(layername)!.add(colname);
}
}
return result;
}
function writeValue(value: number | boolean | string, pbf?: Protobuf) {
if (typeof value === "string") {
pbf!.writeStringField(1, value);
} else if (typeof value === "boolean") {
pbf!.writeBooleanField(7, value);
} else if (typeof value === "number") {
if (value % 1 !== 0) {
pbf!.writeDoubleField(3, value);
} else if (value < 0) {
pbf!.writeSVarintField(6, value);
} else {
pbf!.writeVarintField(5, value);
}
}
}
function writeProperties(context: LayerWriteContext, pbf?: Protobuf) {
var feature = context.feature;
var names = context.names;
var forLayer = context.forLayer;
var keys = context.keys;
var values = context.values;
var keycache = context.keycache;
var valuecache = context.valuecache;
for (var key in feature!.properties) {
if (
names.has("*") ||
names.has(key) ||
forLayer.has("*") ||
forLayer.has(key)
) {
var value = feature!.properties[key];
var keyIndex = keycache[key];
if (value === null) continue; // don't encode null value properties
if (typeof keyIndex === "undefined") {
keys.push(key);
keyIndex = keys.length - 1;
keycache[key] = keyIndex;
}
pbf!.writeVarint(keyIndex);
var type = typeof value;
if (type !== "string" && type !== "boolean" && type !== "number") {
value = JSON.stringify(value);
}
var valueKey = type + ":" + value;
var valueIndex = valuecache[valueKey];
if (typeof valueIndex === "undefined") {
values.push(value);
valueIndex = values.length - 1;
valuecache[valueKey] = valueIndex;
}
pbf!.writeVarint(valueIndex);
}
}
}
function command(cmd: number, length: number) {
return (length << 3) + (cmd & 0x7);
}
function zigzag(num: number) {
return (num << 1) ^ (num >> 31);
}
function writeGeometry(feature: VectorTileFeature, pbf?: Protobuf) {
var geometry = feature.loadGeometry();
var type = feature.type;
var x = 0;
var y = 0;
var rings = geometry.length;
for (var r = 0; r < rings; r++) {
var ring = geometry[r];
var count = 1;
if (type === 1) {
count = ring.length;
}
pbf!.writeVarint(command(1, count)); // moveto
// do not write polygon closing path as lineto
var lineCount = type === 3 ? ring.length - 1 : ring.length;
for (var i = 0; i < lineCount; i++) {
if (i === 1 && type !== 1) {
pbf!.writeVarint(command(2, lineCount - 1)); // lineto
}
var dx = ring[i].x - x;
var dy = ring[i].y - y;
pbf!.writeVarint(zigzag(dx));
pbf!.writeVarint(zigzag(dy));
x += dx;
y += dy;
}
if (type === 3) {
pbf!.writeVarint(command(7, 1)); // closepath
}
}
}
function writeFeature(context: LayerWriteContext, pbf?: Protobuf) {
var feature = context.feature!;
if (feature.id !== undefined) {
pbf!.writeVarintField(1, feature.id);
}
pbf!.writeMessage(2, writeProperties, context);
pbf!.writeVarintField(3, feature.type);
pbf!.writeMessage(4, writeGeometry, feature);
}
function writeLayer(
l: [VectorTileLayer, Set<string>, Set<string>],
sink?: Protobuf
) {
sink = sink!;
let layer = l[0];
sink.writeVarintField(15, layer.version || 1);
sink.writeStringField(1, layer.name || "");
sink.writeVarintField(5, layer.extent || 4096);
var i;
var context: LayerWriteContext = {
keys: [],
values: [],
keycache: {},
valuecache: {},
feature: undefined,
names: l[1],
forLayer: l[2],
};
for (i = 0; i < layer.length; i++) {
let feature = layer.feature(i);
context.feature = feature;
sink.writeMessage(2, writeFeature, context);
}
// finalize the layer by writing key/values
var keys = context.keys;
for (i = 0; i < keys.length; i++) {
sink.writeStringField(3, keys[i]);
}
var values = context.values;
for (i = 0; i < values.length; i++) {
sink.writeMessage(4, writeValue, values[i]);
}
}
interface LayerWriteContext {
keys: string[];
values: (string | boolean | number)[];
keycache: { [_: string]: number };
valuecache: { [_: string]: number };
feature?: VectorTileFeature;
names: Set<string>;
forLayer: Set<string>;
}
// the resulting blob is a vector tile
// includes only the layers specified by key;
// includes only the named attributes
export const vtfilter = (data: Uint8Array, attrstring?:string): Uint8Array => {
if (!attrstring || attrstring === "" || attrstring === "*") {
return data;
}
const attrs = parseAttributes(attrstring);
const source = new VectorTile(new Protobuf(data));
const sink = new Protobuf();
// WARNING: might change the layer order
for (const layername in source.layers) {
let layer = source.layers[layername];
sink.writeMessage(3, writeLayer, [
layer,
attrs.names,
attrs.byLayer.get(layername) || new Set(),
]);
}
return sink.finish();
};