From 888fc4bc80e52daf87c9b4623d33ed34e73a1637 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 19 Oct 2026 13:59:00 -0400 Subject: [PATCH 01/24] chore(deps): Upgrade maplibre-gl to 5.24.0 --- assets/package-lock.json | 280 ++++++++++++--------------------------- assets/package.json | 2 +- 2 files changed, 87 insertions(+), 195 deletions(-) diff --git a/assets/package-lock.json b/assets/package-lock.json index a4e3b0d5c9..9e538f2fe3 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -33,7 +33,7 @@ "leaflet": "^1.4.0", "leaflet-rotatedmarker": "^0.2.0", "lodash": "^4.17.21", - "maplibre-gl": "4.7.0", + "maplibre-gl": "5.24.0", "mobile-detect": "^1.4.5", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", @@ -5014,31 +5014,6 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", - "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, - "node_modules/@mapbox/geojson-rewind/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@mapbox/jsonlint-lines-primitives": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", @@ -5049,9 +5024,9 @@ } }, "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==", + "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==", "license": "ISC" }, "node_modules/@mapbox/tiny-sdf": { @@ -5067,12 +5042,14 @@ "license": "BSD-2-Clause" }, "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==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz", + "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/point-geometry": "~0.1.0" + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^4.0.2" } }, "node_modules/@mapbox/whoots-js": { @@ -5084,18 +5061,26 @@ "node": ">=6.0.0" } }, + "node_modules/@maplibre/geojson-vt": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.3.0.tgz", + "integrity": "sha512-3Zw2jsU9pIJG3G2CQFbLjWj2MlAHodhD7cSY+15iywUuLmmkgzcKRnueVRZ4AcC/5wOtfhidcJAAzBpoBNs3Xg==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.1.0" + } + }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", - "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz", + "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", + "@mapbox/unitbezier": "^1.0.0", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", - "quickselect": "^2.0.0", - "rw": "^1.3.3", + "quickselect": "^3.0.0", "tinyqueue": "^3.0.0" }, "bin": { @@ -5104,11 +5089,43 @@ "gl-style-validate": "dist/gl-style-validate.mjs" } }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", - "license": "ISC" + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/mlt": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.3.0.tgz", + "integrity": "sha512-7M2O7ABM80Oi8XPkfkrjKB8mOBIxGoVgkA52Mu4RMWoBvvMdagQ522+WxVYujZsMpqIr3BpzrTbXrm4k8lCzCQ==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0" + } + }, + "node_modules/@maplibre/vt-pbf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz", + "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^5.1.0" + } + }, + "node_modules/@maplibre/vt-pbf/node_modules/pbf": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -5819,18 +5836,10 @@ } }, "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==" - }, - "node_modules/@types/geojson-vt": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", - "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" }, "node_modules/@types/googlemaps": { "version": "3.43.3", @@ -5920,23 +5929,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", - "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", - "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, "node_modules/@types/mdast": { "version": "3.0.12", "dev": true, @@ -5979,12 +5971,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", - "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", - "license": "MIT" - }, "node_modules/@types/phoenix": { "version": "1.5.4", "dev": true, @@ -6137,15 +6123,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/supercluster": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", - "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.5", "dev": true, @@ -11824,12 +11801,6 @@ "node": ">=6.9.0" } }, - "node_modules/geojson-vt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.3.tgz", - "integrity": "sha512-jR1MwkLaZGa8Zftct9ZFruyWFrdl9ZyD2OliXNy9Qq5bBPeg5wHVpBQF9p5GjnicSDQqvBVpysxTPKmWdsfWMA==", - "license": "ISC" - }, "node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -12601,6 +12572,7 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "dev": true, "funding": [ { "type": "github", @@ -15534,6 +15506,7 @@ }, "node_modules/kind-of": { "version": "6.0.3", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -15998,37 +15971,30 @@ "dev": true }, "node_modules/maplibre-gl": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.0.tgz", - "integrity": "sha512-hkt7je7NxiMQE8EpCxLWP8t6tkK6SkrMe0hIBjYd4Ar/Q7BOCILxthGmGnU993Mwmkvs2mGiXnVUSOK12DeCzg==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz", + "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/tiny-sdf": "^2.1.0", "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", + "@mapbox/vector-tile": "^2.0.4", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.3.1", - "@types/geojson": "^7946.0.14", - "@types/geojson-vt": "3.2.5", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/pbf": "^3.0.5", - "@types/supercluster": "^7.1.3", - "earcut": "^3.0.0", - "geojson-vt": "^4.0.2", - "gl-matrix": "^3.4.3", - "global-prefix": "^4.0.0", + "@maplibre/geojson-vt": "^6.1.0", + "@maplibre/maplibre-gl-style-spec": "^24.8.1", + "@maplibre/mlt": "^1.1.8", + "@maplibre/vt-pbf": "^4.3.0", + "@types/geojson": "^7946.0.16", + "earcut": "^3.0.2", + "gl-matrix": "^3.4.4", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", - "pbf": "^3.3.0", - "potpack": "^2.0.0", + "pbf": "^4.0.1", + "potpack": "^2.1.0", "quickselect": "^3.0.0", - "supercluster": "^8.0.1", - "tinyqueue": "^3.0.0", - "vt-pbf": "^3.1.3" + "tinyqueue": "^3.0.0" }, "engines": { "node": ">=16.14.0", @@ -16038,53 +16004,6 @@ "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" } }, - "node_modules/maplibre-gl/node_modules/global-prefix": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", - "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", - "license": "MIT", - "dependencies": { - "ini": "^4.1.3", - "kind-of": "^6.0.3", - "which": "^4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/maplibre-gl/node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/maplibre-gl/node_modules/isexe": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/maplibre-gl/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", - "license": "ISC", - "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -17666,12 +17585,11 @@ } }, "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", "license": "BSD-3-Clause", "dependencies": { - "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { @@ -20635,12 +20553,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -22164,15 +22076,6 @@ "url": "https://opencollective.com/postcss/" } }, - "node_modules/supercluster": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", - "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", - "license": "ISC", - "dependencies": { - "kdbush": "^4.0.2" - } - }, "node_modules/supports-color": { "version": "5.5.0", "dev": true, @@ -23420,17 +23323,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", - "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, "node_modules/w3c-hr-time": { "version": "1.0.2", "dev": true, diff --git a/assets/package.json b/assets/package.json index 7b490ecf7e..4e2bea7547 100644 --- a/assets/package.json +++ b/assets/package.json @@ -28,7 +28,7 @@ "leaflet": "^1.4.0", "leaflet-rotatedmarker": "^0.2.0", "lodash": "^4.17.21", - "maplibre-gl": "4.7.0", + "maplibre-gl": "5.24.0", "mobile-detect": "^1.4.5", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", From 276519a0a0e8f60ab63ef35bbe3c7b2bfa770903 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 19 Oct 2026 14:03:16 -0400 Subject: [PATCH 02/24] chore(deps): Install `maplibrex` --- assets/js/app.js | 3 +- assets/package-lock.json | 84 ++++++++++++++++++++++++++--------- assets/package.json | 1 + assets/webpack.config.base.js | 7 ++- mix.exs | 1 + mix.lock | 1 + 6 files changed, 74 insertions(+), 23 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index e7bd44a3ef..b324bb6174 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -17,6 +17,7 @@ import fullstory from "./fullstory"; import googleAnalytics from "./google-analytics"; import googleTranslate from "./google-translate"; import juxtapose from "./juxtapose"; +import { MapHooks } from "maplibrex" import modal from "./modal"; import photoGallery from "./photo-gallery"; import pslPageSetup from "./psl-page-setup.js"; @@ -48,7 +49,7 @@ import storageOptions from "./storage.js"; let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken }, - hooks: { ...DotcomHooks, Map }, + hooks: { ...DotcomHooks, ...MapHooks, Map }, dom: { onBeforeElUpdated(from, to) { /* diff --git a/assets/package-lock.json b/assets/package-lock.json index 9e538f2fe3..f7e1e2bf92 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -34,6 +34,7 @@ "leaflet-rotatedmarker": "^0.2.0", "lodash": "^4.17.21", "maplibre-gl": "5.24.0", + "maplibrex": "file:../deps/maplibrex", "mobile-detect": "^1.4.5", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", @@ -155,6 +156,31 @@ "version": "3.3.1", "extraneous": true }, + "../deps/maplibrex": { + "version": "0.1.0", + "license": "MIT", + "peerDependencies": { + "@deck.gl/aggregation-layers": "^9.0.0", + "@deck.gl/core": "^9.0.0", + "@deck.gl/layers": "^9.0.0", + "@deck.gl/mapbox": "^9.0.0", + "maplibre-gl": ">=5.0.0 <7.0.0" + }, + "peerDependenciesMeta": { + "@deck.gl/aggregation-layers": { + "optional": true + }, + "@deck.gl/core": { + "optional": true + }, + "@deck.gl/layers": { + "optional": true + }, + "@deck.gl/mapbox": { + "optional": true + } + } + }, "../deps/phoenix": { "version": "1.8.15", "license": "MIT", @@ -5030,9 +5056,9 @@ "license": "ISC" }, "node_modules/@mapbox/tiny-sdf": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz", - "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.3.0.tgz", + "integrity": "sha512-gBvcNDBBB66bgQYvnaJ6Jk+VCaMmVq5+IZR2kWPisT88Z8s0kBQt6hfVi8QgRW8Vpg0UWZzBA4ZSyzHHC+VeRA==", "license": "BSD-2-Clause" }, "node_modules/@mapbox/unitbezier": { @@ -5052,6 +5078,18 @@ "pbf": "^4.0.2" } }, + "node_modules/@mapbox/vector-tile/node_modules/pbf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/@mapbox/whoots-js": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", @@ -5115,18 +5153,6 @@ "pbf": "^5.1.0" } }, - "node_modules/@maplibre/vt-pbf/node_modules/pbf": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", - "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", - "license": "BSD-3-Clause", - "dependencies": { - "resolve-protobuf-schema": "^2.1.0" - }, - "bin": { - "pbf": "bin/pbf" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "dev": true, @@ -9914,9 +9940,9 @@ "license": "MIT" }, "node_modules/earcut": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.3.tgz", - "integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.4.tgz", + "integrity": "sha512-FHc6yNpnTORB0tM5kmZ03iD2mDuNakDE2lvf4Hw1XkfxtEHIGVi01B/GqUSwx+NcC1WGjidAfpTXzeALtPtfpg==", "license": "ISC" }, "node_modules/eastasianwidth": { @@ -16004,6 +16030,22 @@ "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" } }, + "node_modules/maplibre-gl/node_modules/pbf": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, + "node_modules/maplibrex": { + "resolved": "../deps/maplibrex", + "link": true + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -17585,9 +17627,9 @@ } }, "node_modules/pbf": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", - "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz", + "integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==", "license": "BSD-3-Clause", "dependencies": { "resolve-protobuf-schema": "^2.1.0" diff --git a/assets/package.json b/assets/package.json index 4e2bea7547..7a67f1aea5 100644 --- a/assets/package.json +++ b/assets/package.json @@ -29,6 +29,7 @@ "leaflet-rotatedmarker": "^0.2.0", "lodash": "^4.17.21", "maplibre-gl": "5.24.0", + "maplibrex": "file:../deps/maplibrex", "mobile-detect": "^1.4.5", "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html", diff --git a/assets/webpack.config.base.js b/assets/webpack.config.base.js index 7b5673ab3a..aa678e4769 100644 --- a/assets/webpack.config.base.js +++ b/assets/webpack.config.base.js @@ -189,7 +189,12 @@ module.exports = { resolve: { alias: { // Metro's Flatpickr import briefly, optionally references jQuery without importing it, which breaks Webpack unless we add this line - jquery: path.resolve(__dirname, "./node_modules/jquery") + jquery: path.resolve(__dirname, "./node_modules/jquery"), + // Optional MaplibreX deck.gl deps; we don't use <.deckgl_layer> + "@deck.gl/core": false, + "@deck.gl/layers": false, + "@deck.gl/aggregation-layers": false, + "@deck.gl/mapbox": false }, symlinks: false, extensions: [".tsx", ".ts", ".jsx", ".js"] diff --git a/mix.exs b/mix.exs index b12f82380b..4967056a4c 100644 --- a/mix.exs +++ b/mix.exs @@ -144,6 +144,7 @@ defmodule Dotcom.Mixfile do {:logster, "~> 2.0.0-rc.5"}, # reverted from 0.4 {:mail, "0.3.1"}, + {:maplibrex, "0.1.0"}, {:mbta_metro, "1.4.0", runtime: false}, {:memoize, "1.4.5"}, {:mox, "1.3.2", [only: [:dev, :test]]}, diff --git a/mix.lock b/mix.lock index 65f1d87ac4..0c40f76a6f 100644 --- a/mix.lock +++ b/mix.lock @@ -77,6 +77,7 @@ "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, "makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"}, "makeup_html": {:hex, :makeup_html, "0.2.0", "9f810da8d43d625ccd3f7ea25997e588fa541d80e0a8c6b895157ad5c7e9ca13", [:mix], [{:makeup, "~> 1.2", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "0856f7beb9a6a642ab1307e06d990fe39f0ba58690d0b8e662aa2e027ba331b2"}, + "maplibrex": {:hex, :maplibrex, "0.1.0", "057926a1861accbc51fff540d401a0c273fcd8990ee6331dbc39ae6c6bcc2c46", [:mix], [{:esbuild, "~> 0.8", [hex: :esbuild, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "3929446619d3a7fece469925e9ff63657a0cb6adf2612f71ff23015e27615a22"}, "mbta_metro": {:hex, :mbta_metro, "1.4.0", "ef4e55419c1c077c9dd858af08aa2f492b05c714c306af236ffd978496780759", [:mix], [{:bandit, "~> 1.12", [hex: :bandit, repo: "hexpm", optional: false]}, {:cva, "~> 0.2", [hex: :cva, repo: "hexpm", optional: false]}, {:esbuild, "~> 0.10", [hex: :esbuild, repo: "hexpm", optional: false]}, {:faker, "~> 0.19", [hex: :faker, repo: "hexpm", optional: false]}, {:floki, "~> 0.38", [hex: :floki, repo: "hexpm", optional: false]}, {:gettext, "~> 1.0", [hex: :gettext, repo: "hexpm", optional: false]}, {:heroicons, "~> 0.5", [hex: :heroicons, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.8", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.2", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_storybook, "~> 1.5", [hex: :phoenix_storybook, repo: "hexpm", optional: false]}, {:tailwind, "~> 0.3", [hex: :tailwind, repo: "hexpm", optional: false]}], "hexpm", "213390b1d002de757dc907413f7370155b86c4e33681f2ed0754b51f96914031"}, "mdex": {:hex, :mdex, "0.14.2", "8d2b376f3de8f9f03a2ba0992eeb4f76d58fa790b0ed78dc90bcb5422ce7ff5d", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:lumis, "~> 0.10", [hex: :lumis, repo: "hexpm", optional: true]}, {:mdex_native, ">= 0.2.11", [hex: :mdex_native, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.0 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: true]}], "hexpm", "7003e60b8621ab1c9b3671221a8dcc27dee9f9086026c25ea7aed4057d07978f"}, "mdex_native": {:hex, :mdex_native, "0.2.11", "439f8448bd349611dccce197c8822fc1e2d63907e346f3a68527905bae91c6d7", [:mix], [{:rustler, "~> 0.32", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.8", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "97e5c201fa016ad4dbd948a8014e314fc2d2e15a7fe88e73994ecfdd51a5dd08"}, From d2c4c911a440bb4de6c30e07d8d3f6f27873b1bd Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 19 Oct 2026 17:09:19 -0400 Subject: [PATCH 03/24] feat(robot): Add route-shape lines to the new map --- assets/css/app.scss | 1 + lib/dotcom_web/live/line_diagram_live.ex | 64 +++++++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/assets/css/app.scss b/assets/css/app.scss index 25fa38beec..962117e940 100644 --- a/assets/css/app.scss +++ b/assets/css/app.scss @@ -1,5 +1,6 @@ @use "../node_modules/maplibre-gl/dist/maplibre-gl.css"; @import "../../deps/mbta_metro/priv/dist/metro.css"; +@import "../../deps/maplibrex/assets/css/maplibrex.css"; @import "./map.css"; @tailwind base; diff --git a/lib/dotcom_web/live/line_diagram_live.ex b/lib/dotcom_web/live/line_diagram_live.ex index 01e5b292ab..2fbcf7db4e 100644 --- a/lib/dotcom_web/live/line_diagram_live.ex +++ b/lib/dotcom_web/live/line_diagram_live.ex @@ -179,6 +179,11 @@ defmodule DotcomWeb.LineDiagramLive do _ = Phoenix.PubSub.unsubscribe(Dotcom.PubSub, socket.assigns.vehicle_topic) end + @impl true + def handle_event(_event, _params, socket) do + {:noreply, socket} + end + @impl true def handle_info( %Phoenix.Socket.Broadcast{topic: "vehicles-v2:" <> _, event: event, payload: payload}, @@ -426,14 +431,24 @@ defmodule DotcomWeb.LineDiagramLive do {~t(Change Direction)} - <.map - map_config={@map_config} - route_patterns={@route_patterns} - map_lines={@map_lines} - map_icons={@map_icons} - vehicle_icons={Map.values(@vehicle_icons)} - move_map={@move_map} - /> +
+ <.map + map_config={@map_config} + route_patterns={@route_patterns} + map_lines={@map_lines} + map_icons={@map_icons} + vehicle_icons={Map.values(@vehicle_icons)} + move_map={@move_map} + /> + + <.new_map + map_style={@map_config.style} + route_patterns={@route_patterns} + map_lines={@map_lines} + map_icons={@map_icons} + vehicle_icons={Map.values(@vehicle_icons)} + /> +
+ + line.color, "line-width" => line.width}} + layout={%{"line-cap" => "round", "line-join" => "round"}} + /> + """ + end + + defp line_to_feature(%{coordinates: coordinates}) do + %{ + "type" => "Feature", + "properties" => %{}, + "geometry" => %{ + "type" => "LineString", + "coordinates" => coordinates + } + } + end + defp route_pdf_sidebar_content(assigns) do ~H"""
From 22d5157c651040a5ab363f3ff108632b9b5414f8 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Mon, 19 Oct 2026 17:11:37 -0400 Subject: [PATCH 04/24] refactor(robot): Consolidate all lines onto a single layer --- lib/dotcom_web/live/line_diagram_live.ex | 33 +++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/dotcom_web/live/line_diagram_live.ex b/lib/dotcom_web/live/line_diagram_live.ex index 2fbcf7db4e..5d8dd3a390 100644 --- a/lib/dotcom_web/live/line_diagram_live.ex +++ b/lib/dotcom_web/live/line_diagram_live.ex @@ -649,25 +649,34 @@ defmodule DotcomWeb.LineDiagramLive do /> line.color, "line-width" => line.width}} - layout={%{"line-cap" => "round", "line-join" => "round"}} + paint={%{"line-color" => ["get", "color"], "line-width" => ["get", "width"]}} + layout={ + %{ + "line-cap" => "round", + "line-join" => "round", + "line-sort-key" => ["-", 10, ["get", "width"]] + } + } /> """ end - defp line_to_feature(%{coordinates: coordinates}) do + # `line-sort-key` draws wider features (outlines) first, since higher keys draw on top, so route colors sit on top. + defp map_lines_to_feature_collection(map_lines) do %{ - "type" => "Feature", - "properties" => %{}, - "geometry" => %{ - "type" => "LineString", - "coordinates" => coordinates - } + "type" => "FeatureCollection", + "features" => + Enum.map(map_lines, fn %{coordinates: coordinates, color: color, width: width} -> + %{ + "type" => "Feature", + "properties" => %{"color" => color, "width" => width}, + "geometry" => %{"type" => "LineString", "coordinates" => coordinates} + } + end) } end From 1c230a89e0955ba7edbaa14c4f6117f732425666 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Tue, 20 Oct 2026 13:35:20 -0400 Subject: [PATCH 05/24] feat(robot): Add stop markers hook --- assets/js/app.js | 3 +- assets/js/maplibrex-stop-markers.js | 62 ++++++++++++++++++++++++ lib/dotcom_web/live/line_diagram_live.ex | 17 +++++++ 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 assets/js/maplibrex-stop-markers.js diff --git a/assets/js/app.js b/assets/js/app.js index b324bb6174..aa7bf2520a 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -18,6 +18,7 @@ import googleAnalytics from "./google-analytics"; import googleTranslate from "./google-translate"; import juxtapose from "./juxtapose"; import { MapHooks } from "maplibrex" +import StopMarkers from "./maplibrex-stop-markers"; import modal from "./modal"; import photoGallery from "./photo-gallery"; import pslPageSetup from "./psl-page-setup.js"; @@ -49,7 +50,7 @@ import storageOptions from "./storage.js"; let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken }, - hooks: { ...DotcomHooks, ...MapHooks, Map }, + hooks: { ...DotcomHooks, ...MapHooks, Map, StopMarkers }, dom: { onBeforeElUpdated(from, to) { /* diff --git a/assets/js/maplibrex-stop-markers.js b/assets/js/maplibrex-stop-markers.js new file mode 100644 index 0000000000..a123bf38a2 --- /dev/null +++ b/assets/js/maplibrex-stop-markers.js @@ -0,0 +1,62 @@ +import maplibregl from "maplibre-gl"; +import { MapManager } from "maplibrex"; + +/** + * Hook that places custom HTML elements on a MaplibreX map. + * + * Set `data-map-id` to the id of the map. Each child with a + * `data-coordinates` attribute (JSON `[lng, lat]`) becomes a marker, using a + * clone of that child as the marker element. The optional `data-popup` + * attribute is the HTML for a popup that opens when the marker is clicked. + */ +const StopMarkers = { + mounted() { + this.markers = []; + this.renderMarkers(); + }, + + updated() { + this.renderMarkers(); + }, + + destroyed() { + this.removeMarkers(); + }, + + removeMarkers() { + this.markers.forEach(marker => marker.remove()); + this.markers = []; + }, + + renderMarkers() { + this.removeMarkers(); + + const map = MapManager.get(this.el.dataset.mapId); + if (!map) return; + + this.el.querySelectorAll("[data-coordinates]").forEach(source => { + const coordinates = JSON.parse(source.getAttribute("data-coordinates")); + if (coordinates.length !== 2) return; + + const marker = new maplibregl.Marker({ + anchor: source.getAttribute("data-anchor") || "center", + element: source.cloneNode(true) + }).setLngLat(coordinates); + + const popupHtml = source.getAttribute("data-popup"); + if (popupHtml) { + marker.setPopup( + new maplibregl.Popup({ + className: "m-schedule-line__stop-popup", + focusAfterOpen: false + }).setHTML(popupHtml) + ); + } + + marker.addTo(map); + this.markers.push(marker); + }); + } +}; + +export default StopMarkers; diff --git a/lib/dotcom_web/live/line_diagram_live.ex b/lib/dotcom_web/live/line_diagram_live.ex index 5d8dd3a390..12fec5858d 100644 --- a/lib/dotcom_web/live/line_diagram_live.ex +++ b/lib/dotcom_web/live/line_diagram_live.ex @@ -662,9 +662,26 @@ defmodule DotcomWeb.LineDiagramLive do } } /> + + """ end + defp render_popup(%Phoenix.LiveView.Rendered{} = heex) do + heex + |> Phoenix.HTML.Safe.to_iodata() + |> IO.iodata_to_binary() + end + # `line-sort-key` draws wider features (outlines) first, since higher keys draw on top, so route colors sit on top. defp map_lines_to_feature_collection(map_lines) do %{ From caf18ef7c1d28dfbf7a7a0f13f3bdc4b83eb1a39 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Tue, 20 Oct 2026 14:02:17 -0400 Subject: [PATCH 06/24] refactor(robot): Make the icon marker JS more general-purpose --- assets/js/app.js | 4 +-- ...p-markers.js => maplibrex-icon-markers.js} | 12 +++++--- lib/dotcom_web/live/line_diagram_live.ex | 28 ++++++++++++++++--- 3 files changed, 34 insertions(+), 10 deletions(-) rename assets/js/{maplibrex-stop-markers.js => maplibrex-icon-markers.js} (77%) diff --git a/assets/js/app.js b/assets/js/app.js index aa7bf2520a..75c75f02df 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -18,7 +18,7 @@ import googleAnalytics from "./google-analytics"; import googleTranslate from "./google-translate"; import juxtapose from "./juxtapose"; import { MapHooks } from "maplibrex" -import StopMarkers from "./maplibrex-stop-markers"; +import IconMarkers from "./maplibrex-icon-markers"; import modal from "./modal"; import photoGallery from "./photo-gallery"; import pslPageSetup from "./psl-page-setup.js"; @@ -50,7 +50,7 @@ import storageOptions from "./storage.js"; let liveSocket = new LiveSocket("/live", Socket, { params: { _csrf_token: csrfToken }, - hooks: { ...DotcomHooks, ...MapHooks, Map, StopMarkers }, + hooks: { ...DotcomHooks, ...MapHooks, Map, IconMarkers }, dom: { onBeforeElUpdated(from, to) { /* diff --git a/assets/js/maplibrex-stop-markers.js b/assets/js/maplibrex-icon-markers.js similarity index 77% rename from assets/js/maplibrex-stop-markers.js rename to assets/js/maplibrex-icon-markers.js index a123bf38a2..358b5788cb 100644 --- a/assets/js/maplibrex-stop-markers.js +++ b/assets/js/maplibrex-icon-markers.js @@ -6,10 +6,13 @@ import { MapManager } from "maplibrex"; * * Set `data-map-id` to the id of the map. Each child with a * `data-coordinates` attribute (JSON `[lng, lat]`) becomes a marker, using a - * clone of that child as the marker element. The optional `data-popup` - * attribute is the HTML for a popup that opens when the marker is clicked. + * clone of that child as the marker element. Optional attributes on each child: + * + * - `data-anchor`: marker anchor, e.g. "center" (default) or "bottom" + * - `data-rotation`: marker rotation in degrees + * - `data-popup`: HTML for a popup that opens when the marker is clicked */ -const StopMarkers = { +const IconMarkers = { mounted() { this.markers = []; this.renderMarkers(); @@ -40,6 +43,7 @@ const StopMarkers = { const marker = new maplibregl.Marker({ anchor: source.getAttribute("data-anchor") || "center", + rotation: Number(source.getAttribute("data-rotation")) || 0, element: source.cloneNode(true) }).setLngLat(coordinates); @@ -59,4 +63,4 @@ const StopMarkers = { } }; -export default StopMarkers; +export default IconMarkers; diff --git a/lib/dotcom_web/live/line_diagram_live.ex b/lib/dotcom_web/live/line_diagram_live.ex index 12fec5858d..97b490e049 100644 --- a/lib/dotcom_web/live/line_diagram_live.ex +++ b/lib/dotcom_web/live/line_diagram_live.ex @@ -663,25 +663,45 @@ defmodule DotcomWeb.LineDiagramLive do } /> -