store current location in pinia

This commit is contained in:
Will Freeman
2024-12-22 21:11:38 -08:00
parent 53db867385
commit 7844a66b63
8 changed files with 384 additions and 357 deletions
+203 -141
View File
@@ -12,6 +12,7 @@
"axios": "^1.7.7", "axios": "^1.7.7",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"pinia": "^2.3.0",
"vue": "^3.4.29", "vue": "^3.4.29",
"vue-router": "^4.3.3", "vue-router": "^4.3.3",
"vuetify": "^3.7.2" "vuetify": "^3.7.2"
@@ -47,11 +48,11 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.26.1", "version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
"integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
"dependencies": { "dependencies": {
"@babel/types": "^7.26.0" "@babel/types": "^7.26.3"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@@ -61,9 +62,9 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.26.0", "version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"dependencies": { "dependencies": {
"@babel/helper-string-parser": "^7.25.9", "@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9" "@babel/helper-validator-identifier": "^7.25.9"
@@ -741,49 +742,49 @@
} }
}, },
"node_modules/@vue/compiler-core": { "node_modules/@vue/compiler-core": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
"integrity": "sha512-Uzlxp91EPjfbpeO5KtC0KnXPkuTfGsNDeaKQJxQN718uz+RqDYarEf7UhQJGK+ZYloD2taUbHTI2J4WrUaZQNA==", "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
"dependencies": { "dependencies": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"node_modules/@vue/compiler-dom": { "node_modules/@vue/compiler-dom": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
"integrity": "sha512-GUNHWvoDSbSa5ZSHT9SnV5WkStWfzJwwTd6NMGzilOE/HM5j+9EB9zGXdtu/fCNEmctBqMs6C9SvVPpVPuk1Eg==", "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
"dependencies": { "dependencies": {
"@vue/compiler-core": "3.5.8", "@vue/compiler-core": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"node_modules/@vue/compiler-sfc": { "node_modules/@vue/compiler-sfc": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
"integrity": "sha512-taYpngQtSysrvO9GULaOSwcG5q821zCoIQBtQQSx7Uf7DxpR6CIHR90toPr9QfDD2mqHQPCSgoWBvJu0yV9zjg==", "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"dependencies": { "dependencies": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.8", "@vue/compiler-core": "3.5.13",
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/compiler-ssr": "3.5.8", "@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.11", "magic-string": "^0.30.11",
"postcss": "^8.4.47", "postcss": "^8.4.48",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"node_modules/@vue/compiler-ssr": { "node_modules/@vue/compiler-ssr": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
"integrity": "sha512-W96PtryNsNG9u0ZnN5Q5j27Z/feGrFV6zy9q5tzJVyJaLiwYxvC0ek4IXClZygyhjm+XKM7WD9pdKi/wIRVC/Q==", "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"node_modules/@vue/compiler-vue2": { "node_modules/@vue/compiler-vue2": {
@@ -826,49 +827,49 @@
} }
}, },
"node_modules/@vue/reactivity": { "node_modules/@vue/reactivity": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
"integrity": "sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==", "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
"dependencies": { "dependencies": {
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"node_modules/@vue/runtime-core": { "node_modules/@vue/runtime-core": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
"integrity": "sha512-fJuPelh64agZ8vKkZgp5iCkPaEqFJsYzxLk9vSC0X3G8ppknclNDr61gDc45yBGTaN5Xqc1qZWU3/NoaBMHcjQ==", "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.8", "@vue/reactivity": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"node_modules/@vue/runtime-dom": { "node_modules/@vue/runtime-dom": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
"integrity": "sha512-DpAUz+PKjTZPUOB6zJgkxVI3GuYc2iWZiNeeHQUw53kdrparSTG6HeXUrYDjaam8dVsCdvQxDz6ZWxnyjccUjQ==", "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
"dependencies": { "dependencies": {
"@vue/reactivity": "3.5.8", "@vue/reactivity": "3.5.13",
"@vue/runtime-core": "3.5.8", "@vue/runtime-core": "3.5.13",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
"node_modules/@vue/server-renderer": { "node_modules/@vue/server-renderer": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
"integrity": "sha512-7AmC9/mEeV9mmXNVyUIm1a1AjUhyeeGNbkLh39J00E7iPeGks8OGRB5blJiMmvqSh8SkaS7jkLWSpXtxUCeagA==", "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
"dependencies": { "dependencies": {
"@vue/compiler-ssr": "3.5.8", "@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "3.5.8" "vue": "3.5.13"
} }
}, },
"node_modules/@vue/shared": { "node_modules/@vue/shared": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
"integrity": "sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==" "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
}, },
"node_modules/@vue/tsconfig": { "node_modules/@vue/tsconfig": {
"version": "0.5.1", "version": "0.5.1",
@@ -1111,9 +1112,9 @@
} }
}, },
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.11", "version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
"dependencies": { "dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
@@ -1234,9 +1235,9 @@
} }
}, },
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
}, },
"node_modules/pidtree": { "node_modules/pidtree": {
"version": "0.6.0", "version": "0.6.0",
@@ -1250,10 +1251,31 @@
"node": ">=0.10" "node": ">=0.10"
} }
}, },
"node_modules/pinia": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz",
"integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==",
"dependencies": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.47", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@@ -1270,7 +1292,7 @@
], ],
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.1.0", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {
@@ -1465,15 +1487,15 @@
"dev": true "dev": true
}, },
"node_modules/vue": { "node_modules/vue": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.8.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
"integrity": "sha512-hvuvuCy51nP/1fSRvrrIqTLSvrSyz2Pq+KQ8S8SXCxTWVE0nMaOnSDnSOxV1eYmGfvK7mqiwvd1C59CEEz7dAQ==", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/compiler-sfc": "3.5.8", "@vue/compiler-sfc": "3.5.13",
"@vue/runtime-dom": "3.5.8", "@vue/runtime-dom": "3.5.13",
"@vue/server-renderer": "3.5.8", "@vue/server-renderer": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
}, },
"peerDependencies": { "peerDependencies": {
"typescript": "*" "typescript": "*"
@@ -1484,6 +1506,31 @@
} }
} }
}, },
"node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-router": { "node_modules/vue-router": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz",
@@ -1572,17 +1619,17 @@
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==" "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ=="
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.26.1", "version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz",
"integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==",
"requires": { "requires": {
"@babel/types": "^7.26.0" "@babel/types": "^7.26.3"
} }
}, },
"@babel/types": { "@babel/types": {
"version": "7.26.0", "version": "7.26.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz",
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==",
"requires": { "requires": {
"@babel/helper-string-parser": "^7.25.9", "@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9" "@babel/helper-validator-identifier": "^7.25.9"
@@ -1948,49 +1995,49 @@
} }
}, },
"@vue/compiler-core": { "@vue/compiler-core": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz",
"integrity": "sha512-Uzlxp91EPjfbpeO5KtC0KnXPkuTfGsNDeaKQJxQN718uz+RqDYarEf7UhQJGK+ZYloD2taUbHTI2J4WrUaZQNA==", "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==",
"requires": { "requires": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"entities": "^4.5.0", "entities": "^4.5.0",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"@vue/compiler-dom": { "@vue/compiler-dom": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz",
"integrity": "sha512-GUNHWvoDSbSa5ZSHT9SnV5WkStWfzJwwTd6NMGzilOE/HM5j+9EB9zGXdtu/fCNEmctBqMs6C9SvVPpVPuk1Eg==", "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==",
"requires": { "requires": {
"@vue/compiler-core": "3.5.8", "@vue/compiler-core": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"@vue/compiler-sfc": { "@vue/compiler-sfc": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz",
"integrity": "sha512-taYpngQtSysrvO9GULaOSwcG5q821zCoIQBtQQSx7Uf7DxpR6CIHR90toPr9QfDD2mqHQPCSgoWBvJu0yV9zjg==", "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==",
"requires": { "requires": {
"@babel/parser": "^7.25.3", "@babel/parser": "^7.25.3",
"@vue/compiler-core": "3.5.8", "@vue/compiler-core": "3.5.13",
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/compiler-ssr": "3.5.8", "@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"estree-walker": "^2.0.2", "estree-walker": "^2.0.2",
"magic-string": "^0.30.11", "magic-string": "^0.30.11",
"postcss": "^8.4.47", "postcss": "^8.4.48",
"source-map-js": "^1.2.0" "source-map-js": "^1.2.0"
} }
}, },
"@vue/compiler-ssr": { "@vue/compiler-ssr": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz",
"integrity": "sha512-W96PtryNsNG9u0ZnN5Q5j27Z/feGrFV6zy9q5tzJVyJaLiwYxvC0ek4IXClZygyhjm+XKM7WD9pdKi/wIRVC/Q==", "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==",
"requires": { "requires": {
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"@vue/compiler-vue2": { "@vue/compiler-vue2": {
@@ -2025,46 +2072,46 @@
} }
}, },
"@vue/reactivity": { "@vue/reactivity": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
"integrity": "sha512-mlgUyFHLCUZcAYkqvzYnlBRCh0t5ZQfLYit7nukn1GR96gc48Bp4B7OIcSfVSvlG1k3BPfD+p22gi1t2n9tsXg==", "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==",
"requires": { "requires": {
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"@vue/runtime-core": { "@vue/runtime-core": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz",
"integrity": "sha512-fJuPelh64agZ8vKkZgp5iCkPaEqFJsYzxLk9vSC0X3G8ppknclNDr61gDc45yBGTaN5Xqc1qZWU3/NoaBMHcjQ==", "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==",
"requires": { "requires": {
"@vue/reactivity": "3.5.8", "@vue/reactivity": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"@vue/runtime-dom": { "@vue/runtime-dom": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz",
"integrity": "sha512-DpAUz+PKjTZPUOB6zJgkxVI3GuYc2iWZiNeeHQUw53kdrparSTG6HeXUrYDjaam8dVsCdvQxDz6ZWxnyjccUjQ==", "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==",
"requires": { "requires": {
"@vue/reactivity": "3.5.8", "@vue/reactivity": "3.5.13",
"@vue/runtime-core": "3.5.8", "@vue/runtime-core": "3.5.13",
"@vue/shared": "3.5.8", "@vue/shared": "3.5.13",
"csstype": "^3.1.3" "csstype": "^3.1.3"
} }
}, },
"@vue/server-renderer": { "@vue/server-renderer": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz",
"integrity": "sha512-7AmC9/mEeV9mmXNVyUIm1a1AjUhyeeGNbkLh39J00E7iPeGks8OGRB5blJiMmvqSh8SkaS7jkLWSpXtxUCeagA==", "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==",
"requires": { "requires": {
"@vue/compiler-ssr": "3.5.8", "@vue/compiler-ssr": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"@vue/shared": { "@vue/shared": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.8.tgz", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz",
"integrity": "sha512-mJleSWbAGySd2RJdX1RBtcrUBX6snyOc0qHpgk3lGi4l9/P/3ny3ELqFWqYdkXIwwNN/kdm8nD9ky8o6l/Lx2A==" "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="
}, },
"@vue/tsconfig": { "@vue/tsconfig": {
"version": "0.5.1", "version": "0.5.1",
@@ -2247,9 +2294,9 @@
"requires": {} "requires": {}
}, },
"magic-string": { "magic-string": {
"version": "0.30.11", "version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
"integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
"requires": { "requires": {
"@jridgewell/sourcemap-codec": "^1.5.0" "@jridgewell/sourcemap-codec": "^1.5.0"
} }
@@ -2327,9 +2374,9 @@
"dev": true "dev": true
}, },
"picocolors": { "picocolors": {
"version": "1.1.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
}, },
"pidtree": { "pidtree": {
"version": "0.6.0", "version": "0.6.0",
@@ -2337,13 +2384,22 @@
"integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
"dev": true "dev": true
}, },
"pinia": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.0.tgz",
"integrity": "sha512-ohZj3jla0LL0OH5PlLTDMzqKiVw2XARmC1XYLdLWIPBMdhDW/123ZWr4zVAhtJm+aoSkFa13pYXskAvAscIkhQ==",
"requires": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
}
},
"postcss": { "postcss": {
"version": "8.4.47", "version": "8.4.49",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"requires": { "requires": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.1.0", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
} }
}, },
@@ -2451,17 +2507,23 @@
"dev": true "dev": true
}, },
"vue": { "vue": {
"version": "3.5.8", "version": "3.5.13",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.8.tgz", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz",
"integrity": "sha512-hvuvuCy51nP/1fSRvrrIqTLSvrSyz2Pq+KQ8S8SXCxTWVE0nMaOnSDnSOxV1eYmGfvK7mqiwvd1C59CEEz7dAQ==", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==",
"requires": { "requires": {
"@vue/compiler-dom": "3.5.8", "@vue/compiler-dom": "3.5.13",
"@vue/compiler-sfc": "3.5.8", "@vue/compiler-sfc": "3.5.13",
"@vue/runtime-dom": "3.5.8", "@vue/runtime-dom": "3.5.13",
"@vue/server-renderer": "3.5.8", "@vue/server-renderer": "3.5.13",
"@vue/shared": "3.5.8" "@vue/shared": "3.5.13"
} }
}, },
"vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"requires": {}
},
"vue-router": { "vue-router": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz",
+1
View File
@@ -15,6 +15,7 @@
"axios": "^1.7.7", "axios": "^1.7.7",
"countup.js": "^2.8.0", "countup.js": "^2.8.0",
"leaflet.markercluster": "^1.5.3", "leaflet.markercluster": "^1.5.3",
"pinia": "^2.3.0",
"vue": "^3.4.29", "vue": "^3.4.29",
"vue-router": "^4.3.3", "vue-router": "^4.3.3",
"vuetify": "^3.7.2" "vuetify": "^3.7.2"
+12 -8
View File
@@ -11,7 +11,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, h, createApp, watch } from 'vue'; import { onMounted, h, createApp, watch, type PropType } from 'vue';
import L, { type LatLngExpression } from 'leaflet'; import L, { type LatLngExpression } from 'leaflet';
import DFMapPopup from './DFMapPopup.vue'; import DFMapPopup from './DFMapPopup.vue';
@@ -32,7 +32,7 @@ const props = defineProps({
}, },
alprs: Array, alprs: Array,
currentLocation: { currentLocation: {
type: Object, type: Object as PropType<[number, number] | null>,
default: null, default: null,
}, },
}); });
@@ -60,12 +60,17 @@ function initializeMap() {
} }
function renderCurrentLocation() { function renderCurrentLocation() {
if (!props.currentLocation)
return;
else
console.log('Current location:', props.currentLocation);
if (currentLocationLayer) { if (currentLocationLayer) {
map.removeLayer(currentLocationLayer); map.removeLayer(currentLocationLayer);
} }
currentLocationLayer = L.featureGroup(); currentLocationLayer = L.featureGroup();
const clMarker = L.circleMarker([props.currentLocation.lat, props.currentLocation.lng], { const clMarker = L.circleMarker([props.currentLocation[0], props.currentLocation[1]], {
radius: 10, radius: 10,
color: '#ffffff', color: '#ffffff',
fillColor: '#007bff', fillColor: '#007bff',
@@ -82,9 +87,7 @@ function renderCurrentLocation() {
function populateMap() { function populateMap() {
const showFov = props.zoom >= 16; const showFov = props.zoom >= 16;
if (props.currentLocation) { renderCurrentLocation();
renderCurrentLocation();
}
if (clusterLayer) { if (clusterLayer) {
map.removeLayer(clusterLayer); map.removeLayer(clusterLayer);
@@ -212,6 +215,7 @@ function registerWatchers() {
}); });
watch(() => props.currentLocation, (newLocation, oldLocation) => { watch(() => props.currentLocation, (newLocation, oldLocation) => {
console.log("current location watcher triggered!");
renderCurrentLocation(); renderCurrentLocation();
}); });
} }
@@ -243,8 +247,8 @@ function registerWatchers() {
.bottomright { .bottomright {
position: absolute; position: absolute;
bottom: 50px; bottom: 50px; /* hack */
right: 60px; right: 60px; /* hack */
z-index: 1000; z-index: 1000;
} }
</style> </style>
+3
View File
@@ -1,6 +1,7 @@
import './assets/main.css' import './assets/main.css'
import { createApp } from 'vue' import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue' import App from './App.vue'
import router from './router' import router from './router'
@@ -18,9 +19,11 @@ const vuetify = createVuetify({
} }
}) })
const pinia = createPinia()
const app = createApp(App) const app = createApp(App)
app.use(router) app.use(router)
app.use(vuetify) app.use(vuetify)
app.use(pinia)
app.mount('#app') app.mount('#app')
+32
View File
@@ -0,0 +1,32 @@
import { defineStore } from 'pinia';
import { ref, type Ref } from 'vue';
export const useGlobalStore = defineStore('global', () => {
const currentLocation: Ref<[number, number] | null> = ref(null);
const setCurrentLocation = (): Promise<[number, number]> =>
new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
currentLocation.value = [position.coords.latitude, position.coords.longitude];
resolve([position.coords.latitude, position.coords.longitude]);
},
(error) => {
reject(error);
},
{
timeout: 10000,
enableHighAccuracy: true,
}
);
} else {
reject(new Error('Geolocation is not supported by this browser.'));
}
});
return {
currentLocation,
setCurrentLocation,
};
});
+113 -142
View File
@@ -1,76 +1,76 @@
<template> <template>
<!-- Hero Section --> <!-- Hero Section -->
<v-container fluid class="hero-section"> <v-container fluid class="hero-section">
<v-row justify="center"> <v-row justify="center">
<v-col cols="12" md="8" class="text-center"> <v-col cols="12" md="8" class="text-center">
<h1 class="display-1 px-8">You're Being Tracked by ALPRs!</h1> <h1 class="display-1 px-8">You're Being Tracked by ALPRs!</h1>
<ALPRCounter class="mt-4" /> <ALPRCounter class="mt-4" />
<p class="subtitle-1 px-8 mt-6 mb-12"> <p class="subtitle-1 px-8 mt-6 mb-12">
Automated License Plate Readers (ALPRs) are monitoring your every move. Learn more about how they work and how you can protect your privacy. Automated License Plate Readers (ALPRs) are monitoring your every move. Learn more about how they work and how you can protect your privacy.
</p> </p>
<v-btn color="rgb(18, 151, 195)" large @click="goToMap({ withCurrentLocation: true })"> <v-btn color="rgb(18, 151, 195)" large @click="goToMap({ withCurrentLocation: true })">
Find Nearby ALPRs Find Nearby ALPRs
<v-icon end>mdi-map</v-icon> <v-icon end>mdi-map</v-icon>
</v-btn> </v-btn>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
<!-- Information Section --> <!-- Information Section -->
<v-container class="info-section py-10"> <v-container class="info-section py-10">
<v-row class="align-center"> <v-row class="align-center">
<v-col cols="12" md="4" class="text-center"> <v-col cols="12" md="4" class="text-center">
<v-card> <v-card>
<v-card-title class="headline"> <v-card-title class="headline">
<v-icon x-large class="mr-2">mdi-shield-alert</v-icon> <v-icon x-large class="mr-2">mdi-shield-alert</v-icon>
Privacy Violations Privacy Violations
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
ALPRs track your movements and store your data for long periods of time, creating a detailed record of your location history. They surveil mostly innocent people while claiming to target criminals. ALPRs track your movements and store your data for long periods of time, creating a detailed record of your location history. They surveil mostly innocent people while claiming to target criminals.
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-col> </v-col>
<v-col cols="12" md="4" class="text-center"> <v-col cols="12" md="4" class="text-center">
<v-card> <v-card>
<v-card-title class="headline"> <v-card-title class="headline">
<v-icon x-large class="mr-2">mdi-alert-circle</v-icon> <v-icon x-large class="mr-2">mdi-alert-circle</v-icon>
Risk of Misuse Risk of Misuse
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
Data from ALPRs has led to <a target="_blank" href="https://www.newsobserver.com/news/state/north-carolina/article287381160.html">wrongful arrests</a>, profiling, and <a target="_blank" href="https://www.kwch.com/2022/10/31/kechi-police-lieutenant-arrested-using-police-technology-stalk-wife/">stalking ex-partners</a> by police officers. Data from ALPRs has led to <a target="_blank" href="https://www.newsobserver.com/news/state/north-carolina/article287381160.html">wrongful arrests</a>, profiling, and <a target="_blank" href="https://www.kwch.com/2022/10/31/kechi-police-lieutenant-arrested-using-police-technology-stalk-wife/">stalking ex-partners</a> by police officers.
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-col> </v-col>
<v-col cols="12" md="4" class="text-center"> <v-col cols="12" md="4" class="text-center">
<v-card> <v-card>
<v-card-title class="headline"> <v-card-title class="headline">
<v-icon x-large class="mr-2">mdi-handcuffs</v-icon> <v-icon x-large class="mr-2">mdi-handcuffs</v-icon>
Limited Benefits Limited Benefits
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
There's no substantial evidence that ALPRs effectively prevent crime, despite <a target="_blank" href="https://www.404media.co/researcher-who-oversaw-flock-surveillance-study-now-has-concerns-about-it/">Flock's unethical attempts</a> to prove otherwise. There's no substantial evidence that ALPRs effectively prevent crime, despite <a target="_blank" href="https://www.404media.co/researcher-who-oversaw-flock-surveillance-study-now-has-concerns-about-it/">Flock's unethical attempts</a> to prove otherwise.
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-col> </v-col>
</v-row> </v-row>
</v-container> </v-container>
<!-- Map Section --> <!-- Map Section -->
<v-container class="map-section py-10 text-center"> <v-container class="map-section py-10 text-center">
<h2 class="display-2 mb-4">Explore ALPR Locations Near You</h2> <h2 class="display-2 mb-4">Explore ALPR Locations Near You</h2>
<v-btn color="white" large @click="goToMap"> <v-btn color="white" large @click="goToMap">
View the Map View the Map
<v-icon end>mdi-map</v-icon> <v-icon end>mdi-map</v-icon>
</v-btn> </v-btn>
</v-container> </v-container>
<v-container> <v-container>
<v-footer class="text-center"> <v-footer class="text-center">
<span class="attribution"> <span class="attribution">
Maps ©&nbsp;<a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>&nbsp;contributors. Maps ©&nbsp;<a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>&nbsp;contributors.
</span> </span>
</v-footer> </v-footer>
</v-container> </v-container>
</template> </template>
<style> <style>
@@ -80,22 +80,22 @@
color: white; color: white;
padding: 100px 0 !important; padding: 100px 0 !important;
position: relative; position: relative;
} }
.hero-section::before { .hero-section::before {
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
z-index: 1; z-index: 1;
} }
.hero-section > * { .hero-section > * {
position: relative; position: relative;
z-index: 2; z-index: 2;
} }
.info-section { .info-section {
@@ -103,83 +103,54 @@
} }
.map-section { .map-section {
background: url('/deflock-screenshot.webp') no-repeat center center; background: url('/deflock-screenshot.webp') no-repeat center center;
background-size: cover; background-size: cover;
color: white; color: white;
padding: 100px 0; padding: 100px 0;
position: relative; position: relative;
} }
.map-section::before { .map-section::before {
content: ''; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.4); background: rgba(0, 0, 0, 0.4);
z-index: 1; z-index: 1;
} }
.map-section > * { .map-section > * {
position: relative; position: relative;
z-index: 2; z-index: 2;
} }
</style> </style>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import ALPRCounter from '@/components/ALPRCounter.vue'; import ALPRCounter from '@/components/ALPRCounter.vue';
import { useGlobalStore } from '@/stores/global';
const router = useRouter(); const router = useRouter();
const userLocation = ref<[number, number] | null>(null); const { setCurrentLocation } = useGlobalStore();
async function fetchUserLocation(): Promise<[number, number]> {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
resolve([position.coords.latitude, position.coords.longitude]);
},
(error) => {
reject(error);
},
{
timeout: 10000,
enableHighAccuracy: true,
}
);
} else {
reject(new Error('Geolocation is not supported by this browser.'));
}
});
}
async function getUserLocation() {
try {
const [lat, lon] = await fetchUserLocation();
userLocation.value = [lat, lon];
} catch (error) {
console.debug('Error fetching user location:', error);
}
}
interface GoToMapOptions { interface GoToMapOptions {
withCurrentLocation?: boolean; withCurrentLocation?: boolean;
} }
async function goToMap(options: GoToMapOptions = {}) { async function goToMap(options: GoToMapOptions = {}) {
if (options.withCurrentLocation) { if (options.withCurrentLocation) {
await getUserLocation(); setCurrentLocation()
if (userLocation.value) { .then((currentLocation) => {
const [lat, lon] = userLocation.value; const [lat, lon] = currentLocation;
router.push({ path: '/map', hash: `#map=14/${lat.toFixed(6)}/${lon.toFixed(6)}` }); router.push({ path: '/map', hash: `#map=12/${lat.toFixed(6)}/${lon.toFixed(6)}` });
} else { })
router.push({ path: '/map', hash: '#map=14/40.0150/-105.2705' }); .catch(() => {
} router.push({ path: '/map' });
} else { });
router.push({ path: '/map' }); } else {
} router.push({ path: '/map' });
}
} }
</script> </script>
+11 -28
View File
@@ -58,6 +58,7 @@ import { BoundingBox } from '@/services/apiService';
import type { Cluster } from '@/services/apiService'; import type { Cluster } from '@/services/apiService';
import { getALPRs, geocodeQuery, getClusters } from '@/services/apiService'; import { getALPRs, geocodeQuery, getClusters } from '@/services/apiService';
import { useDisplay, useTheme } from 'vuetify'; import { useDisplay, useTheme } from 'vuetify';
import { useGlobalStore } from '@/stores/global';
import type { ALPR } from '@/types'; import type { ALPR } from '@/types';
import L from 'leaflet'; import L from 'leaflet';
globalThis.L = L; globalThis.L = L;
@@ -74,7 +75,6 @@ const center: Ref<any|null> = ref(null);
const bounds: Ref<BoundingBox|null> = ref(null); const bounds: Ref<BoundingBox|null> = ref(null);
const searchField: Ref<any|null> = ref(null); const searchField: Ref<any|null> = ref(null);
const searchQuery: Ref<string> = ref(''); const searchQuery: Ref<string> = ref('');
const currentLocation: Ref<any|null> = ref(null);
const router = useRouter(); const router = useRouter();
const { xs } = useDisplay(); const { xs } = useDisplay();
@@ -92,6 +92,11 @@ const bboxForLastRequest: Ref<BoundingBox|null> = ref(null);
const showClusters = computed(() => zoom.value <= CLUSTER_ZOOM_THRESHOLD); const showClusters = computed(() => zoom.value <= CLUSTER_ZOOM_THRESHOLD);
const isLoadingALPRs = ref(false); const isLoadingALPRs = ref(false);
const globalStore = useGlobalStore();
const setCurrentLocation = globalStore.setCurrentLocation;
const currentLocation = computed(() => globalStore.currentLocation);
const visibleALPRs = computed(() => { const visibleALPRs = computed(() => {
return alprs.value.filter(alpr => bounds.value?.containsPoint(alpr.lat, alpr.lon)); return alprs.value.filter(alpr => bounds.value?.containsPoint(alpr.lat, alpr.lon));
}); });
@@ -122,38 +127,16 @@ function onSearch() {
} }
function goToUserLocation() { function goToUserLocation() {
getUserLocation() setCurrentLocation()
.then(location => { .then((cl) => {
console.log('User location:', location); center.value = cl;
center.value = { lat: location[0], lng: location[1] };
zoom.value = DEFAULT_ZOOM; zoom.value = DEFAULT_ZOOM;
}).catch(error => { })
.catch(error => {
console.debug('Error getting user location.', error); console.debug('Error getting user location.', error);
}); });
} }
function getUserLocation(): Promise<[number, number]> {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
currentLocation.value = { lat: position.coords.latitude, lng: position.coords.longitude };
resolve([position.coords.latitude, position.coords.longitude]);
},
(error) => {
reject(error);
},
{
timeout: 10000,
enableHighAccuracy: true,
}
);
} else {
reject(new Error('Geolocation is not supported by this browser.'));
}
});
};
function updateBounds(newBounds: any) { function updateBounds(newBounds: any) {
updateURL(); updateURL();
+9 -38
View File
@@ -128,42 +128,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import ALPRCounter from '@/components/ALPRCounter.vue'; import ALPRCounter from '@/components/ALPRCounter.vue';
import { useGlobalStore } from '@/stores/global';
const router = useRouter(); const router = useRouter();
const userLocation = ref<[number, number] | null>(null); const { setCurrentLocation } = useGlobalStore();
async function fetchUserLocation(): Promise<[number, number]> {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
resolve([position.coords.latitude, position.coords.longitude]);
},
(error) => {
reject(error);
},
{
timeout: 10000,
enableHighAccuracy: true,
}
);
} else {
reject(new Error('Geolocation is not supported by this browser.'));
}
});
}
async function getUserLocation() {
try {
const [lat, lon] = await fetchUserLocation();
userLocation.value = [lat, lon];
} catch (error) {
console.debug('Error fetching user location:', error);
}
}
interface GoToMapOptions { interface GoToMapOptions {
withCurrentLocation?: boolean; withCurrentLocation?: boolean;
@@ -171,13 +141,14 @@ interface GoToMapOptions {
async function goToMap(options: GoToMapOptions = {}) { async function goToMap(options: GoToMapOptions = {}) {
if (options.withCurrentLocation) { if (options.withCurrentLocation) {
await getUserLocation(); setCurrentLocation()
if (userLocation.value) { .then((currentLocation) => {
const [lat, lon] = userLocation.value; const [lat, lon] = currentLocation;
router.push({ path: '/map', hash: `#map=16/${lat.toFixed(6)}/${lon.toFixed(6)}` }); router.push({ path: '/map', hash: `#map=16/${lat.toFixed(6)}/${lon.toFixed(6)}` });
} else { })
router.push({ path: '/map', hash: '#map=16/40.0150/-105.2705' }); .catch(() => {
} router.push({ path: '/map' });
});
} else { } else {
router.push({ path: '/map' }); router.push({ path: '/map' });
} }