diff --git a/Gruntfile.js b/Gruntfile.js index 001061ed1..9914c74f1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,5 +1,6 @@ module.exports = function(grunt) { const Diff = require('diff'); + const nodeResolve = require('@rollup/plugin-node-resolve'); grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), cssmin: { @@ -94,6 +95,13 @@ module.exports = function(grunt) { src: ['index.html'], dest: 'dist/' }, + { + expand: true, + flatten: true, + filter: 'isFile', + src: ['src/pmtilesRules.js'], + dest: 'dist/' + }, { expand: true, cwd: 'node_modules/leaflet.locatecontrol/src/', @@ -199,7 +207,9 @@ module.exports = function(grunt) { }, rollup: { options: { - format: 'iife' + format: 'iife', + plugins: [nodeResolve()], + external: './pmtilesRules.js' }, main: { dest: 'dist/mapml.js', diff --git a/package-lock.json b/package-lock.json index 33f9d4886..e84971874 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "W3C", "devDependencies": { "@playwright/test": "^1.39.0", + "@rollup/plugin-node-resolve": "^15.2.3", "diff": "^5.1.0", "express": "^4.17.1", "grunt": "^1.4.0", @@ -22,14 +23,33 @@ "grunt-contrib-watch": "~1.1.0", "grunt-html": "^15.2.2", "grunt-prettier": "^2.2.0", - "grunt-rollup": "^11.3.0", + "grunt-rollup": "^12.0.0", "leaflet": "^1.9.4", "leaflet.locatecontrol": "^0.79.0", "path": "^0.12.7", "playwright": "^1.39.0", "proj4": "^2.6.2", "proj4leaflet": "^1.0.2", - "rollup": "^2.23.1" + "protomaps-leaflet": "4.0.0", + "rollup": "^2.23.1", + "serve-static": "^1.15.0" + } + }, + "node_modules/@mapbox/point-geometry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", + "dev": true + }, + "node_modules/@mapbox/vector-tile": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.3.tgz", + "integrity": "sha512-Fq8PzBAaBeG3sEZA7Bomlv+8ZJcS5KCD6MRlCqiFrroOLnwZFFSJVydk1J9sneScJq9q4yyGfxKa+i7x2TLG8A==", + "dev": true, + "dependencies": { + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.14", + "pbf": "^4.0.1" } }, "node_modules/@playwright/test": { @@ -47,6 +67,86 @@ "node": ">=16" } }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==", + "dev": true + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true + }, + "node_modules/@types/leaflet": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -235,6 +335,18 @@ "node": ">=8" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", @@ -317,6 +429,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "dev": true + }, "node_modules/colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -433,6 +551,15 @@ "ms": "^2.1.1" } }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -590,6 +717,12 @@ "node": ">=4" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -709,6 +842,12 @@ "node": ">=0.4.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, "node_modules/figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -883,11 +1022,28 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gaze": { "version": "1.1.3", @@ -1659,15 +1815,15 @@ } }, "node_modules/grunt-rollup": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/grunt-rollup/-/grunt-rollup-11.3.0.tgz", - "integrity": "sha512-RDRbq2Vw28/nJdFprZP/odl1uv9wQ0cN+JMEOwtjt8Nsvho0ODOe9ME8Jl22D1Z5A2P8G0mFr2Gia7IwgV53XA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/grunt-rollup/-/grunt-rollup-12.0.0.tgz", + "integrity": "sha512-f2aYSxUiSf7cylK/WoOBx5DQmM+XTZzdO3xe3YHssaKwHQQLSMxGV0j/eIfrvjhRFWPBf+CCW18iSt/hapawpQ==", "dev": true, "dependencies": { - "rollup": "^2.21.0" + "rollup": "^2.71.1" }, "engines": { - "node": ">=8.6.0" + "node": ">=12" }, "peerDependencies": { "grunt": ">=0.4.0" @@ -1733,6 +1889,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -1851,13 +2019,31 @@ "node": ">=0.10.0" } }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1884,6 +2070,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2164,12 +2356,12 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -2460,6 +2652,18 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, + "node_modules/pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "dev": true, + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -2508,6 +2712,36 @@ "node": ">=16" } }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pmtiles": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.0.7.tgz", + "integrity": "sha512-kGTFNyzmNdF8yiGxuoskNwLfUkHzC1ouJ5waL6dLID+g4hKlGnohIGX4q7qXrY0rHTK+8MwIKDTrUjY70Kk5ew==", + "dev": true, + "dependencies": { + "@types/leaflet": "^1.9.8", + "fflate": "^0.8.0" + } + }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "dev": true + }, "node_modules/prettier": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", @@ -2560,6 +2794,28 @@ "proj4": "^2.3.14" } }, + "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==", + "dev": true + }, + "node_modules/protomaps-leaflet": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/protomaps-leaflet/-/protomaps-leaflet-4.0.0.tgz", + "integrity": "sha512-AE9P2+ks/HxjshX9gHFUVUQ2MfTIDP52jKfweVF9nxCFWNNO6qknHQGCk/v39o0oSEEwkpuASl+xn9SVF/tEbg==", + "dev": true, + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/vector-tile": "^2.0.2", + "@types/css-font-loading-module": "^0.0.7", + "color2k": "^2.0.3", + "pbf": "^4.0.1", + "pmtiles": "^3.0.7", + "potpack": "^1.0.2", + "rbush": "^3.0.1" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2588,6 +2844,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -2610,6 +2872,15 @@ "node": ">= 0.8.0" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -2635,12 +2906,12 @@ } }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -2651,6 +2922,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "dev": true, + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -2664,9 +2944,9 @@ } }, "node_modules/rollup": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.1.tgz", - "integrity": "sha512-Heyl885+lyN/giQwxA8AYT2GY3U+gOlTqVLrMQYno8Z1X9lAOpfXPiKiZCyPc25e9BLJM3Zlh957dpTlO4pa8A==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -2675,7 +2955,7 @@ "node": ">=10.0.0" }, "optionalDependencies": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.2" } }, "node_modules/safe-buffer": { @@ -3069,6 +3349,23 @@ } }, "dependencies": { + "@mapbox/point-geometry": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", + "dev": true + }, + "@mapbox/vector-tile": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.3.tgz", + "integrity": "sha512-Fq8PzBAaBeG3sEZA7Bomlv+8ZJcS5KCD6MRlCqiFrroOLnwZFFSJVydk1J9sneScJq9q4yyGfxKa+i7x2TLG8A==", + "dev": true, + "requires": { + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.14", + "pbf": "^4.0.1" + } + }, "@playwright/test": { "version": "1.39.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.39.0.tgz", @@ -3078,6 +3375,64 @@ "playwright": "1.39.0" } }, + "@rollup/plugin-node-resolve": { + "version": "15.2.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", + "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.2.1", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + } + }, + "@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@types/css-font-loading-module": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/css-font-loading-module/-/css-font-loading-module-0.0.7.tgz", + "integrity": "sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==", + "dev": true + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/geojson": { + "version": "7946.0.14", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "dev": true + }, + "@types/leaflet": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", + "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", + "dev": true, + "requires": { + "@types/geojson": "*" + } + }, + "@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3242,6 +3597,12 @@ "fill-range": "^7.1.1" } }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, "bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", @@ -3311,6 +3672,12 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color2k": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/color2k/-/color2k-2.0.3.tgz", + "integrity": "sha512-zW190nQTIoXcGCaU08DvVNFTmQhUpnJfVuAKfWqUQkflXKpaDdpaYoM0iluLS9lgJNHyBF58KKA2FBEwkD7wog==", + "dev": true + }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -3400,6 +3767,12 @@ "ms": "^2.1.1" } }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3524,6 +3897,12 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -3619,6 +3998,12 @@ "websocket-driver": ">=0.5.1" } }, + "fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true + }, "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -3763,10 +4148,17 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true + }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "gaze": { @@ -4362,12 +4754,12 @@ } }, "grunt-rollup": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/grunt-rollup/-/grunt-rollup-11.3.0.tgz", - "integrity": "sha512-RDRbq2Vw28/nJdFprZP/odl1uv9wQ0cN+JMEOwtjt8Nsvho0ODOe9ME8Jl22D1Z5A2P8G0mFr2Gia7IwgV53XA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/grunt-rollup/-/grunt-rollup-12.0.0.tgz", + "integrity": "sha512-f2aYSxUiSf7cylK/WoOBx5DQmM+XTZzdO3xe3YHssaKwHQQLSMxGV0j/eIfrvjhRFWPBf+CCW18iSt/hapawpQ==", "dev": true, "requires": { - "rollup": "^2.21.0" + "rollup": "^2.71.1" } }, "has": { @@ -4400,6 +4792,15 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -4500,13 +4901,22 @@ "is-windows": "^1.0.1" } }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, "is-core-module": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", - "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dev": true, "requires": { - "has": "^1.0.3" + "hasown": "^2.0.2" } }, "is-extglob": { @@ -4524,6 +4934,12 @@ "is-extglob": "^2.1.1" } }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4749,12 +5165,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -4970,6 +5386,15 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", "dev": true }, + "pbf": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz", + "integrity": "sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==", + "dev": true, + "requires": { + "resolve-protobuf-schema": "^2.1.0" + } + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -4990,6 +5415,15 @@ "requires": { "fsevents": "2.3.2", "playwright-core": "1.39.0" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } } }, "playwright-core": { @@ -4998,6 +5432,22 @@ "integrity": "sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw==", "dev": true }, + "pmtiles": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.0.7.tgz", + "integrity": "sha512-kGTFNyzmNdF8yiGxuoskNwLfUkHzC1ouJ5waL6dLID+g4hKlGnohIGX4q7qXrY0rHTK+8MwIKDTrUjY70Kk5ew==", + "dev": true, + "requires": { + "@types/leaflet": "^1.9.8", + "fflate": "^0.8.0" + } + }, + "potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==", + "dev": true + }, "prettier": { "version": "2.8.4", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", @@ -5035,6 +5485,28 @@ "proj4": "^2.3.14" } }, + "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==", + "dev": true + }, + "protomaps-leaflet": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/protomaps-leaflet/-/protomaps-leaflet-4.0.0.tgz", + "integrity": "sha512-AE9P2+ks/HxjshX9gHFUVUQ2MfTIDP52jKfweVF9nxCFWNNO6qknHQGCk/v39o0oSEEwkpuASl+xn9SVF/tEbg==", + "dev": true, + "requires": { + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/vector-tile": "^2.0.2", + "@types/css-font-loading-module": "^0.0.7", + "color2k": "^2.0.3", + "pbf": "^4.0.1", + "pmtiles": "^3.0.7", + "potpack": "^1.0.2", + "rbush": "^3.0.1" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -5054,6 +5526,12 @@ "side-channel": "^1.0.4" } }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", + "dev": true + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -5070,6 +5548,15 @@ "string_decoder": "0.10" } }, + "rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dev": true, + "requires": { + "quickselect": "^2.0.0" + } + }, "readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", @@ -5092,16 +5579,25 @@ } }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } }, + "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==", + "dev": true, + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -5112,12 +5608,12 @@ } }, "rollup": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.1.tgz", - "integrity": "sha512-Heyl885+lyN/giQwxA8AYT2GY3U+gOlTqVLrMQYno8Z1X9lAOpfXPiKiZCyPc25e9BLJM3Zlh957dpTlO4pa8A==", + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, "requires": { - "fsevents": "~2.1.2" + "fsevents": "~2.3.2" } }, "safe-buffer": { diff --git a/package.json b/package.json index a58ba0f5f..cb8a774cc 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ }, "devDependencies": { "@playwright/test": "^1.39.0", + "@rollup/plugin-node-resolve": "^15.2.3", "diff": "^5.1.0", "express": "^4.17.1", "grunt": "^1.4.0", @@ -48,14 +49,16 @@ "grunt-contrib-watch": "~1.1.0", "grunt-html": "^15.2.2", "grunt-prettier": "^2.2.0", - "grunt-rollup": "^11.3.0", + "grunt-rollup": "^12.0.0", "leaflet": "^1.9.4", "leaflet.locatecontrol": "^0.79.0", "path": "^0.12.7", "playwright": "^1.39.0", "proj4": "^2.6.2", "proj4leaflet": "^1.0.2", - "rollup": "^2.23.1" + "protomaps-leaflet": "4.0.0", + "rollup": "^2.23.1", + "serve-static": "^1.15.0" }, "files": [ "dist", diff --git a/playwright.config.js b/playwright.config.js index f8d7545b7..7b9d3f897 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -1,15 +1,16 @@ -const config = { - timeout: 30000, - testDir: './test/e2e', - webServer: { - command: 'node test/server.js', - port: 30001, - timeout: 10000 - }, - use: { - headless: true, - browserName: 'chromium', - baseURL: 'http://localhost:30001/' - } -}; -module.exports = config; \ No newline at end of file +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + timeout: 30000, + testDir: './test/e2e', + webServer: { + command: 'node test/server.js', + port: 30001, + timeout: 10000 + }, + use: { + headless: true, + browserName: 'chromium', + baseURL: 'http://localhost:30001/' + } +}); \ No newline at end of file diff --git a/src/map-link.js b/src/map-link.js index e0c8d488a..f3b9381d2 100644 --- a/src/map-link.js +++ b/src/map-link.js @@ -22,7 +22,10 @@ export class MapLink extends HTMLElement { } set type(val) { // improve this - if (val === 'text/mapml' || val.startsWith('image/')) { + if ( + val === 'text/mapml' || + val.startsWith('image/' || val === 'application/pmtiles') + ) { this.setAttribute('type', val); } } @@ -259,7 +262,24 @@ export class MapLink extends HTMLElement { //this._createLegendLink(); break; case 'stylesheet': - this._createStylesheetLink(); + // MIME type application/pmtiles+stylesheet is an invention of the requirement to get + // closer to loading style rules as CSS does, via link / (map-link) + // we could probably do something similar with map-style i.e. treat the + // content of map-style as though it was a stylesheet tbd caveat CSP + if (this.type === 'application/pmtiles+stylesheet') { + const pmtilesStyles = new URL(this.href, this.getBase()).href; + import(pmtilesStyles) + .then((module) => { + this._pmtilesRules = module.pmtilesRules; + }) + .catch((reason) => { + console.error( + 'Error importing pmtiles symbolizer rules or theme: \n' + reason + ); + }); + } else { + this._createStylesheetLink(); + } break; case 'alternate': this._createAlternateLink(); // add media attribute @@ -346,7 +366,36 @@ export class MapLink extends HTMLElement { 'map-link[rel=image],map-link[rel=tile],map-link[rel=features]' ) ).indexOf(this); - if (this.rel === 'tile') { + if ( + (this.rel === 'tile' && this.type === 'application/pmtiles') || + this.type === 'application/vnd.mapbox-vector-tile' + ) { + let relativeSelector = + 'map-link[rel="stylesheet"][type="application/pmtiles+stylesheet"]'; + let rules = M.getClosest( + this, + 'map-extent:has(' + + relativeSelector + + '),layer-:has(' + + relativeSelector + + '),mapml-viewer:has(' + + relativeSelector + + ')' + )?.querySelector(relativeSelector)._pmtilesRules; + let options = { + zoomBounds: this.getZoomBounds(), + extentBounds: this.getBounds(), + crs: M[this.parentExtent.units], + zIndex: this.zIndex, + pane: this.parentExtent._extentLayer.getContainer(), + linkEl: this, + pmtilesRules: rules + }; + this._templatedLayer = M.templatedPMTilesLayer( + this._templateVars, + options + ).addTo(this.parentExtent._extentLayer); + } else if (this.rel === 'tile') { this._templatedLayer = M.templatedTileLayer(this._templateVars, { zoomBounds: this.getZoomBounds(), extentBounds: this.getBounds(), @@ -897,10 +946,16 @@ export class MapLink extends HTMLElement { case 'alternate': ready = '_alternate'; break; + case 'stylesheet': + if (this.type === 'application/pmtiles+stylesheet') { + ready = _pmtilesRules; + } else { + resolve(); + } + break; case 'zoomin': case 'zoomout': case 'legend': - case 'stylesheet': case 'license': resolve(); break; diff --git a/src/mapml/index.js b/src/mapml/index.js index 07fb6e138..fbd37c4b8 100644 --- a/src/mapml/index.js +++ b/src/mapml/index.js @@ -54,6 +54,11 @@ import { TemplatedImageLayer, templatedImageLayer } from './layers/TemplatedImageLayer'; +import * as protomapsL from '../../node_modules/protomaps-leaflet/dist/esm/index.js'; +import { + TemplatedPMTilesLayer, + templatedPMTilesLayer +} from './layers/TemplatedPMTilesLayer'; import { ImageLayer, imageLayer } from './layers/ImageLayer'; import { MapMLLayer, mapMLLayer } from './layers/MapMLLayer'; import { DebugOverlay, debugOverlay } from './layers/DebugOverlay'; @@ -928,4 +933,7 @@ import { DOMTokenList } from './utils/DOMTokenList'; M.BLANK_TT_TREF = 'mapmltemplatedtileplaceholder'; M.DOMTokenList = DOMTokenList; + M.protomapsL = protomapsL; + M.templatedPMTilesLayer = templatedPMTilesLayer; + M.TemplatedPMTilesLayer = TemplatedPMTilesLayer; })(window, document); diff --git a/src/mapml/layers/MapMLLayer.js b/src/mapml/layers/MapMLLayer.js index a3ff2eef1..b606d9c7b 100644 --- a/src/mapml/layers/MapMLLayer.js +++ b/src/mapml/layers/MapMLLayer.js @@ -338,8 +338,8 @@ export var MapMLLayer = L.LayerGroup.extend({ if (mapml.querySelector('map-title')) { layer._title = mapml.querySelector('map-title').textContent.trim(); layer._titleIsReadOnly = true; - } else if (mapml instanceof Element && mapml.hasAttribute('label')) { - layer._title = mapml.getAttribute('label').trim(); + } else if (layer._layerEl && layer._layerEl.hasAttribute('label')) { + layer._title = layer._layerEl.getAttribute('label').trim(); } } function parseLicenseAndLegend() { diff --git a/src/mapml/layers/TemplatedPMTilesLayer.js b/src/mapml/layers/TemplatedPMTilesLayer.js new file mode 100644 index 000000000..f7863d4c3 --- /dev/null +++ b/src/mapml/layers/TemplatedPMTilesLayer.js @@ -0,0 +1,125 @@ +/* global L */ + +export var TemplatedPMTilesLayer = L.Layer.extend({ + initialize: function (template, options) { + /* structure of this._template: + { + template: decodeURI(new URL(template, this.getBase())), + linkEl: (map-link), + rel: (map-link@rel), + type: (map-link@type), + values: [map-input], + inputsReady: Promise.allSettled(inputsReady), + zoom: (map-input@type=zoom), + projection: (map-extent@units), + tms: true/false, + step: step + } + */ + this._template = template; + this._container = L.DomUtil.create( + 'div', + 'leaflet-layer mapml-pmtiles-container' + ); + this._pmtilesOptions = { + pane: this._container, + maxDataZoom: template.zoom?.max ?? 15, + url: this._mapInputNamesToProtomapsUrl(template), + noWrap: true + }; + + let paintRules = options?.pmtilesRules?.get(this._template.template); + if (paintRules?.rules) { + L.extend(this._pmtilesOptions, { + paintRules: paintRules.rules.PAINT_RULES + }); + L.extend(this._pmtilesOptions, { + labelRules: paintRules.rules.LABEL_RULES + }); + } else if (paintRules?.theme?.theme) { + L.extend(this._pmtilesOptions, { theme: paintRules.theme.theme }); + } else { + console.warn( + 'pmtiles symbolizer rules or theme not found for map-link@tref -> ' + + this._template.template + ); + } + this.zoomBounds = options.zoomBounds; + this.extentBounds = options.extentBounds; + // get rid of duplicate info, it can be confusing + delete options.zoomBounds; + delete options.extentBounds; + this._linkEl = options.linkEl; + L.setOptions(this, options); + }, + /** + * + * @param {type} template + * @returns {url compatible with protomaps-leaflet {z},{x},{y}} + */ + _mapInputNamesToProtomapsUrl: function (template) { + // protomaps requires hard-coded URL template variables {z}, {x} and {y} + // MapML allows you to set your own variable names, so we have to map the + // names you use for zoom, column and row to the {z}, {x} and {y} variables, + // and then replace the variable names used in the template with the + // corresponding {z}, {x} and {y} strings for protomaps + let url = template.template; + let re = new RegExp( + template.zoom?.name ? '{' + template.zoom.name + '}' : '{z}', + 'ig' + ); + url = url.replace(re, '{z}'); + let rowName = template.values.find( + (i) => i.type === 'location' && i.axis === 'row' + )?.name; + re = new RegExp(rowName ? '{' + rowName + '}' : '{y}', 'ig'); + url = url.replace(re, '{y}'); + let colName = template.values.find( + (i) => i.type === 'location' && i.axis === 'column' + )?.name; + re = new RegExp(colName ? '{' + colName + '}' : '{x}', 'ig'); + url = url.replace(re, '{x}'); + return url; + }, + onAdd: function (map) { + this._map = map; + this.options.pane.appendChild(this._container); + this.setZIndex(this.options.zIndex); + this._pmtilesLayer = M.protomapsL + .leafletLayer(this._pmtilesOptions) + .addTo(map); + }, + onRemove: function (map) { + this._pmtilesLayer.remove(); + L.DomUtil.remove(this._container); + }, + isVisible: function () { + if (this._template.projection !== 'OSMTILE') return false; + let map = this._linkEl.getMapEl()._map; + let mapZoom = map.getZoom(); + let mapBounds = M.pixelToPCRSBounds( + map.getPixelBounds(), + mapZoom, + map.options.projection + ); + return ( + mapZoom <= this.zoomBounds.maxZoom && + mapZoom >= this.zoomBounds.minZoom && + this.extentBounds.overlaps(mapBounds) + ); + }, + setZIndex: function (zIndex) { + this.options.zIndex = zIndex; + if ( + this._container && + this.options.zIndex !== undefined && + this.options.zIndex !== null + ) { + this._container.style.zIndex = this.options.zIndex; + } + return this; + } +}); +export var templatedPMTilesLayer = function (template, options) { + return new TemplatedPMTilesLayer(template, options); +}; diff --git a/src/pmtilesRules.js b/src/pmtilesRules.js new file mode 100644 index 000000000..21221cebd --- /dev/null +++ b/src/pmtilesRules.js @@ -0,0 +1,174 @@ +/* global M */ + +/* + * The following imports from protomaps-leaflet are available on the + * M.protomapsL global variable e.g. M.protomapsL.CenteredSymbolizer : + + CenteredSymbolizer, + CenteredTextSymbolizer, + CircleSymbolizer, + FlexSymbolizer, + Font, + GeomType, + GroupSymbolizer, + IconSymbolizer, + Index, + Justify, + Labeler, + Labelers, + LineLabelPlacement, + LineLabelSymbolizer, + LineSymbolizer, + OffsetSymbolizer, + OffsetTextSymbolizer, + Padding, + PmtilesSource, + PolygonSymbolizer, + Sheet, + ShieldSymbolizer, + Static, + TextPlacements, + TextSymbolizer, + TileCache, + View, + ZxySource, + arr, + covering, + createPattern, + exp, + getZoom, + isCcw, + isInRing, + labelRules, + leafletLayer, + linear, + paint, + paintRules, + pointInPolygon, + pointMinDistToLines, + pointMinDistToPoints, + sourcesToViews, + step, + toIndex, + transformGeom, + wrap +*/ + +/* You can use the above imports, as well as define your own symbolizers and rules, + * as described here: https://docs.protomaps.com/pmtiles/leaflet, and as + * exemplified below. + */ + +class SpearfishSymbolizer { + constructor(options) { + this.color = options.color; + this.shape = options.shape; + } + draw(context, geom, z, feature) { + let pt = geom[0][0]; + context.fillStyle = this.color; + context.strokeStyle = this.color; + context.beginPath(); + if (this.shape === 'circle') { + context.arc(pt.x, pt.y, 3, 0, 2 * Math.PI); + } else { + context.rect(pt.x - 2, pt.y - 2, 4, 4); + } + context.stroke(); + context.fill(); + } +} +/* + To add your own rules or themes, add a key exactly matching the URL template + you use in your HTML or text/mapml document's + + or + Note that the ?theme=dark|light has no effect on the pmtiles service, it is + only added below to make different URL keys to the pmtilesRules Map. + + */ +const pmtilesRules = new Map(); +pmtilesRules.set( + 'https://data.source.coop/protomaps/openstreetmap/tiles/v3.pmtiles', + { theme: { theme: 'light' } } +); +pmtilesRules.set( + 'https://data.source.coop/protomaps/openstreetmap/tiles/v3.pmtiles?theme=light', + { theme: { theme: 'light' } } +); +pmtilesRules.set( + 'https://data.source.coop/protomaps/openstreetmap/tiles/v3.pmtiles?theme=dark', + { theme: { theme: 'dark' } } +); +pmtilesRules.set( + 'https://api.protomaps.com/tiles/v3/{bing}/{bong}/{bang}.mvt?key=41392fb7515533a5', + { theme: { theme: 'light' } } +); +pmtilesRules.set( + 'http://localhost:8080/geoserver/gwc/service/wmts/rest/spearfish/OSMTILE/{foo}/{baz}/{bar}?format=application/vnd.mapbox-vector-tile', + { + rules: { + PAINT_RULES: [ + { + dataLayer: 'streams', + symbolizer: new M.protomapsL.LineSymbolizer({ + color: 'steelblue', + width: 2 + }) + }, + { + dataLayer: 'roads', + symbolizer: new M.protomapsL.LineSymbolizer({ + color: 'maroon', + width: 2 + }) + }, + { + dataLayer: 'restricted', + symbolizer: new M.protomapsL.PolygonSymbolizer({ + fill: 'red', + opacity: 0.5 + }) + }, + { + dataLayer: 'restricted', + symbolizer: new M.protomapsL.LineSymbolizer({ + color: 'red', + width: 2 + }) + }, + { + dataLayer: 'archsites', + symbolizer: new SpearfishSymbolizer({ + color: 'red', + shape: 'square' + }) + }, + { + dataLayer: 'bugsites', + symbolizer: new SpearfishSymbolizer({ + color: 'black', + shape: 'circle' + }) + } + ], + LABEL_RULES: [ + { + dataLayer: 'archsites', + symbolizer: new M.protomapsL.CenteredTextSymbolizer({ + labelProps: ['str1'], + fill: 'white', + width: 2, + stroke: 'black', + font: '600 16px sans-serif' + }), + // note that filter is a property of a rule, not an option to a symbolizer + filter: (z, f) => { + return f.props.str1.trim().toLowerCase() !== 'no name'; + } + } + ] + } + } +); +export { pmtilesRules }; diff --git a/test/e2e/data/tiles/osmtile/0/0/0.mvt b/test/e2e/data/tiles/osmtile/0/0/0.mvt new file mode 100644 index 000000000..69fb0073c Binary files /dev/null and b/test/e2e/data/tiles/osmtile/0/0/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/1/0/0.mvt b/test/e2e/data/tiles/osmtile/1/0/0.mvt new file mode 100644 index 000000000..5dbd3b9a9 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/1/0/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/1/0/1.mvt b/test/e2e/data/tiles/osmtile/1/0/1.mvt new file mode 100644 index 000000000..6a7df09a9 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/1/0/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/1/1/0.mvt b/test/e2e/data/tiles/osmtile/1/1/0.mvt new file mode 100644 index 000000000..eb91a622c Binary files /dev/null and b/test/e2e/data/tiles/osmtile/1/1/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/1/1/1.mvt b/test/e2e/data/tiles/osmtile/1/1/1.mvt new file mode 100644 index 000000000..1d9d54ac8 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/1/1/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/0/0.mvt b/test/e2e/data/tiles/osmtile/2/0/0.mvt new file mode 100644 index 000000000..4af0a306f Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/0/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/0/1.mvt b/test/e2e/data/tiles/osmtile/2/0/1.mvt new file mode 100644 index 000000000..514992f1f Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/0/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/0/2.mvt b/test/e2e/data/tiles/osmtile/2/0/2.mvt new file mode 100644 index 000000000..0c5b1d701 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/0/2.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/0/3.mvt b/test/e2e/data/tiles/osmtile/2/0/3.mvt new file mode 100644 index 000000000..8f61c6519 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/0/3.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/1/0.mvt b/test/e2e/data/tiles/osmtile/2/1/0.mvt new file mode 100644 index 000000000..8bfd0f270 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/1/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/1/1.mvt b/test/e2e/data/tiles/osmtile/2/1/1.mvt new file mode 100644 index 000000000..797b5ad4e Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/1/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/1/2.mvt b/test/e2e/data/tiles/osmtile/2/1/2.mvt new file mode 100644 index 000000000..c493cbf7e Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/1/2.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/1/3.mvt b/test/e2e/data/tiles/osmtile/2/1/3.mvt new file mode 100644 index 000000000..e55d6aa6f Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/1/3.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/2/0.mvt b/test/e2e/data/tiles/osmtile/2/2/0.mvt new file mode 100644 index 000000000..b8c731537 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/2/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/2/1.mvt b/test/e2e/data/tiles/osmtile/2/2/1.mvt new file mode 100644 index 000000000..10db6b667 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/2/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/2/2.mvt b/test/e2e/data/tiles/osmtile/2/2/2.mvt new file mode 100644 index 000000000..9aaeeee69 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/2/2.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/2/3.mvt b/test/e2e/data/tiles/osmtile/2/2/3.mvt new file mode 100644 index 000000000..77e078331 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/2/3.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/3/0.mvt b/test/e2e/data/tiles/osmtile/2/3/0.mvt new file mode 100644 index 000000000..1b1964050 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/3/0.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/3/1.mvt b/test/e2e/data/tiles/osmtile/2/3/1.mvt new file mode 100644 index 000000000..d4dd7fb79 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/3/1.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/3/2.mvt b/test/e2e/data/tiles/osmtile/2/3/2.mvt new file mode 100644 index 000000000..d4115eb25 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/3/2.mvt differ diff --git a/test/e2e/data/tiles/osmtile/2/3/3.mvt b/test/e2e/data/tiles/osmtile/2/3/3.mvt new file mode 100644 index 000000000..a4275ab60 Binary files /dev/null and b/test/e2e/data/tiles/osmtile/2/3/3.mvt differ diff --git a/test/e2e/layers/pmtilesRulesModule.js b/test/e2e/layers/pmtilesRulesModule.js new file mode 100644 index 000000000..30151f01c --- /dev/null +++ b/test/e2e/layers/pmtilesRulesModule.js @@ -0,0 +1,13 @@ +const pmtilesRules = new Map(); +pmtilesRules.set('http://localhost:30001/spearfish.pmtiles?theme=dark', { + theme: { theme: 'dark' } +}); +pmtilesRules.set( + 'http://localhost:30001/tiles/osmtile/{beans}/{foo}/{bar}.mvt?theme=light', + { theme: { theme: 'light' } } +); +pmtilesRules.set( + 'http://localhost:30001/tiles/osmtile/{z}/{x}/{y}.mvt?theme=light', + { theme: { theme: 'light' } } +); +export { pmtilesRules }; diff --git a/test/e2e/layers/spearfish.pmtiles b/test/e2e/layers/spearfish.pmtiles new file mode 100644 index 000000000..cc3b74fda Binary files /dev/null and b/test/e2e/layers/spearfish.pmtiles differ diff --git a/test/e2e/layers/templatedPMTilesCBMTILETest.html b/test/e2e/layers/templatedPMTilesCBMTILETest.html new file mode 100644 index 000000000..caaeac8c1 --- /dev/null +++ b/test/e2e/layers/templatedPMTilesCBMTILETest.html @@ -0,0 +1,43 @@ + + + + + + templatedPMTilesCBMTILETest.html + + + + + + + + + + + + + + + + + + + + + {z}/{x}/{y}.mvt test + + + + + + + + + + + diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.html b/test/e2e/layers/templatedPMTilesMVTLayer.html new file mode 100644 index 000000000..5fee7b8a9 --- /dev/null +++ b/test/e2e/layers/templatedPMTilesMVTLayer.html @@ -0,0 +1,40 @@ + + + + + + templatedPMTilesMVTLayer.html + + + + + + + PMTiles test dark + + + + + + + + + {z}/{x}/{y}.mvt test + + + + + + + + + + {z}/{x}/{y}.mvt hardcoding test + + + + + + + + diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js b/test/e2e/layers/templatedPMTilesMVTLayer.test.js new file mode 100644 index 000000000..4b6b14e4e --- /dev/null +++ b/test/e2e/layers/templatedPMTilesMVTLayer.test.js @@ -0,0 +1,189 @@ +import { test, expect, chromium } from '@playwright/test'; + +test.describe('Playwright templatedPMTilesLayer Tests', () => { + let page; + let context; + test.beforeAll(async () => { + context = await chromium.launchPersistentContext(''); + page = + context.pages().find((page) => page.url() === 'about:blank') || + (await context.newPage()); + }); + + test.afterAll(async function () { + await context.close(); + }); + + test('Test rendered data', async ({ page }) => { + await page.goto('templatedPMTilesMVTLayer.html'); + await page.waitForTimeout(1000); + const viewer = page.getByTestId('viewer'); + // seems like a lot of pixels to allow to be different, but missing fonts + // across instances can require the snapshots to tolerate differences + // due to that + await expect(viewer).toHaveScreenshot('mvt-light.png', { + maxDiffPixels: 1500 + }); + await viewer.evaluate((v) => v.zoomTo(0, 0, 2)); + await expect(viewer).toHaveScreenshot('mvt-light-z2.png', { + maxDiffPixels: 1500 + }); + const lightLayer = page.getByTestId('light'); + await lightLayer.evaluate((l) => l.removeAttribute('checked')); + await viewer.evaluate((v) => v.reload()); + + await page.waitForTimeout(3000); + const darkLayer = page.getByTestId('dark'); + await darkLayer.evaluate((l) => l.setAttribute('checked', 'checked')); + await expect(viewer).toHaveScreenshot('pmtiles-dark.png', { + maxDiffPixels: 1500 + }); + await darkLayer.evaluate((l) => l.zoomTo()); + await expect(viewer).toHaveScreenshot('pmtiles-dark-z10.png', { + maxDiffPixels: 1500 + }); + }); + test('pmtilesStyles.js module not found console errors', async ({ page }) => { + const messages = []; + page.on('console', async (msg) => { + for (const arg of msg.args()) messages.push(await arg.jsonValue()); + }); + await page.goto('templatedPMTilesMVTLayerMissingStyles.html'); + await page.waitForTimeout(1000); + let errorLoadingModule = false; + let errorFindingRules = false; + for (const m of messages) { + if (m.startsWith('Error importing pmtiles symbolizer rules or theme:')) + errorLoadingModule = true; + if ( + m.startsWith( + 'pmtiles symbolizer rules or theme not found for map-link@tref ->' + ) + ) + errorFindingRules = true; + } + + expect(errorLoadingModule).toBe(true); + expect(errorFindingRules).toBe(true); + messages.length = 0; + await page.goto('templatedPMTilesMVTLayerMissingRuleKey.html'); + await page.waitForTimeout(1000); + errorLoadingModule = false; + errorFindingRules = false; + for (const m of messages) { + if (m.startsWith('Error importing pmtiles symbolizer rules or theme:')) + errorLoadingModule = true; + if ( + m.startsWith( + 'pmtiles symbolizer rules or theme not found for map-link@tref ->' + ) + ) + errorFindingRules = true; + } + expect(errorLoadingModule).toBe(false); + expect(errorFindingRules).toBe(true); + }); + test('A protomaps map-link in a non-OSMTILE map-extent is never enabled', async () => { + await page.goto('templatedPMTilesCBMTILETest.html'); + await page.waitForTimeout(500); + const viewer = page.getByTestId('viewer'); + const flexProjectionLayer = page.getByTestId('flexible-projection-layer'); + const fixedProjectionLayer = page.getByTestId('osmtile-only-layer'); + await expect(flexProjectionLayer).not.toHaveAttribute('disabled'); + await expect(fixedProjectionLayer).toHaveAttribute('disabled'); + await viewer.evaluate((v) => (v.projection = 'OSMTILE')); + await page.waitForTimeout(500); + await expect(flexProjectionLayer).not.toHaveAttribute('disabled'); + await expect(fixedProjectionLayer).not.toHaveAttribute('disabled'); + }); + test('A protomaps map-link, parent map-extent and ancestor layer- are disabled when out of bounds', async ({ + page + }) => { + await page.goto('templatedPMTilesMVTLayer.html'); + await page.waitForTimeout(1000); + const viewer = page.getByTestId('viewer'); + const lightLayer = page.getByTestId('light'); + const darkLayer = page.getByTestId('dark'); + await lightLayer.evaluate((l) => l.removeAttribute('checked')); + await darkLayer.evaluate((l) => { + l.checked = 'checked'; + l.zoomTo(); + }); + // at this point, the dark layer should be enabled + await expect(darkLayer).not.toHaveAttribute('disabled'); + // move horizontally 2x the width of the extent, should make it not visible / disabled + await darkLayer.evaluate((l) => { + let w = + l.extent.bottomRight.gcrs.horizontal - l.extent.topLeft.gcrs.horizontal; + let h = + l.extent.topLeft.gcrs.vertical - l.extent.bottomRight.gcrs.vertical; + let cLat = l.extent.bottomRight.gcrs.vertical + h / 2; + let cLon = l.extent.topLeft.gcrs.horizontal + w / 2; + let z = l.parentElement.zoom; + l.parentElement.zoomTo(cLat, cLon + w * 2, z); + }); + await page.waitForTimeout(500); + // at this point, the dark layer should be disabled + await expect(darkLayer).toHaveAttribute('disabled'); + }); + test('A protomaps mvt map-link that has {z},{x},{y} variable names without associated map-inputs will not render', async ({ + page + }) => { + const messages = []; + page.on('console', async (msg) => { + for (const arg of msg.args()) messages.push(await arg.jsonValue()); + }); + await page.goto('templatedPMTilesMVTLayer.html'); + const viewer = page.getByTestId('viewer'); + const lightLayer = page.getByTestId('light'); + await lightLayer.evaluate((l) => l.removeAttribute('checked')); + const darkLayer = page.getByTestId('dark'); + await darkLayer.evaluate((l) => l.removeAttribute('checked')); + const hardCodedVariablesLayer = page.getByTestId('hard-coded-variables'); + await hardCodedVariablesLayer.evaluate((l) => (l.checked = true)); + await page.waitForTimeout(1000); + await expect(viewer).toHaveScreenshot('mvt-blank.png', { + maxDiffPixels: 1500 + }); + let errorLoadingModule = false; + let errorFindingRules = false; + let errorNoZoomInput = false; + let errorNoXInput = false; + let errorNoYInput = false; + for (const m of messages) { + if (m.startsWith('Error importing pmtiles symbolizer rules or theme:')) + errorLoadingModule = true; + if ( + m.startsWith( + 'pmtiles symbolizer rules or theme not found for map-link@tref ->' + ) + ) + errorFindingRules = true; + if ( + m.startsWith( + 'input with name=z not found for template variable of same name' + ) + ) + errorNoZoomInput = true; + if ( + m.startsWith( + 'input with name=x not found for template variable of same name' + ) + ) + errorNoXInput = true; + if ( + m.startsWith( + 'input with name=y not found for template variable of same name' + ) + ) + errorNoYInput = true; + } + // lack of rendering wasn't due to lack of styles + expect(errorLoadingModule).toBe(false); + expect(errorFindingRules).toBe(false); + // errors on un-mapped template variables are the cause + expect(errorNoZoomInput).toBe(true); + expect(errorNoXInput).toBe(true); + expect(errorNoYInput).toBe(true); + }); +}); diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-linux.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-linux.png new file mode 100644 index 000000000..d0012071e Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-linux.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-win32.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-win32.png new file mode 100644 index 000000000..d0012071e Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-blank-win32.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-linux.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-linux.png new file mode 100644 index 000000000..a5888716b Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-linux.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-win32.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-win32.png new file mode 100644 index 000000000..c3832b970 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-win32.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-linux.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-linux.png new file mode 100644 index 000000000..6b8ed275a Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-linux.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-win32.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-win32.png new file mode 100644 index 000000000..8615239d9 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/mvt-light-z2-win32.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-linux.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-linux.png new file mode 100644 index 000000000..7d9945334 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-linux.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-win32.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-win32.png new file mode 100644 index 000000000..bd6e71054 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-win32.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-linux.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-linux.png new file mode 100644 index 000000000..f4bcae849 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-linux.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-win32.png b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-win32.png new file mode 100644 index 000000000..77f7a6983 Binary files /dev/null and b/test/e2e/layers/templatedPMTilesMVTLayer.test.js-snapshots/pmtiles-dark-z10-win32.png differ diff --git a/test/e2e/layers/templatedPMTilesMVTLayerMissingRuleKey.html b/test/e2e/layers/templatedPMTilesMVTLayerMissingRuleKey.html new file mode 100644 index 000000000..fe75077fc --- /dev/null +++ b/test/e2e/layers/templatedPMTilesMVTLayerMissingRuleKey.html @@ -0,0 +1,24 @@ + + + + + + templatedPMTilesMVTLayerMissingStyles.html + + + + + + + {z}/{x}/{y}.mvt test + + + + + + + + + + + diff --git a/test/e2e/layers/templatedPMTilesMVTLayerMissingStyles.html b/test/e2e/layers/templatedPMTilesMVTLayerMissingStyles.html new file mode 100644 index 000000000..263d764a2 --- /dev/null +++ b/test/e2e/layers/templatedPMTilesMVTLayerMissingStyles.html @@ -0,0 +1,24 @@ + + + + + + templatedPMTilesMVTLayerMissingStyles.html + + + + + + + {z}/{x}/{y}.mvt test + + + + + + + + + + + diff --git a/test/server.js b/test/server.js index 22d184d3e..4ccfd6692 100644 --- a/test/server.js +++ b/test/server.js @@ -1,6 +1,7 @@ const express = require('express'); const app = express(); const path = require('path'); +const serveStatic = require('serve-static'); const port = 30001; //then loads in the index file @@ -18,7 +19,8 @@ app.use(express.static(path.join(__dirname, 'e2e/elements/layer-'))); app.use(express.static(path.join(__dirname, 'e2e/api'))); app.use(express.static(path.join(__dirname, 'e2e/data'))); app.use(express.static(path.join(__dirname, 'e2e/geojson'))); -app.use(express.static(path.join(__dirname, 'e2e/layers'))); +// serveStatic enables byte range requests, only required on this directory +app.use(serveStatic(path.join(__dirname, 'e2e/layers'))); app.use(express.static(path.join(__dirname, 'e2e/mapml-viewer'))); app.use(express.static(path.join(__dirname, 'e2e/web-map'))); diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index b5e9e8048..000000000 --- a/test/setup.js +++ /dev/null @@ -1,2 +0,0 @@ -require('../dist/leaflet.js'); -require('../dist/mapml.js');