Rebuild Leaflet map components from scratch.

This commit is contained in:
Buster Neece 2022-12-11 14:31:37 -06:00
parent e3513049df
commit d51a4ca3d1
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
6 changed files with 268 additions and 72 deletions

View File

@ -16,10 +16,10 @@
"@fullcalendar/luxon2": "^5.10.2",
"@fullcalendar/timegrid": "^5.9.0",
"@fullcalendar/vue3": "^5.11",
"@vue-leaflet/vue-leaflet": "*",
"@vue/compat": "^3.2.45",
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"@vueuse/core": "^9.6.0",
"axios": "^1",
"bootstrap": "^4.6.0 <5",
"bootstrap-notify": "^3.1.3",
@ -1970,14 +1970,10 @@
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz",
"integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw=="
},
"node_modules/@vue-leaflet/vue-leaflet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.7.0.tgz",
"integrity": "sha512-YY1bcg+7ftGZk4ihCOckOSFMfuhimGxEpPabtpBpaaaquABliB0dkANNFx5d8Y5Vadko5Yjc8Vq74hb/dg4gQw==",
"peerDependencies": {
"leaflet": "^1.6.0",
"vue": "^3.0.0"
}
"node_modules/@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"node_modules/@vue/compat": {
"version": "3.2.45",
@ -2183,6 +2179,89 @@
}
}
},
"node_modules/@vueuse/core": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz",
"integrity": "sha512-qGUcjKQXHgN+jqXEgpeZGoxdCbIDCdVPz3QiF1uyecVGbMuM63o96I1GjYx5zskKgRI0FKSNsVWM7rwrRMTf6A==",
"dependencies": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.6.0",
"@vueuse/shared": "9.6.0",
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"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/@vueuse/metadata": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.6.0.tgz",
"integrity": "sha512-sIC8R+kWkIdpi5X2z2Gk8TRYzmczDwHRhEFfCu2P+XW2JdPoXrziqsGpDDsN7ykBx4ilwieS7JUIweVGhvZ93w==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.6.0.tgz",
"integrity": "sha512-/eDchxYYhkHnFyrb00t90UfjCx94kRHxc7J1GtBCqCG4HyPMX+krV9XJgVtWIsAMaxKVU4fC8NSUviG1JkwhUQ==",
"dependencies": {
"vue-demi": "*"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"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/@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@ -11972,11 +12051,10 @@
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.3.tgz",
"integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw=="
},
"@vue-leaflet/vue-leaflet": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@vue-leaflet/vue-leaflet/-/vue-leaflet-0.7.0.tgz",
"integrity": "sha512-YY1bcg+7ftGZk4ihCOckOSFMfuhimGxEpPabtpBpaaaquABliB0dkANNFx5d8Y5Vadko5Yjc8Vq74hb/dg4gQw==",
"requires": {}
"@types/web-bluetooth": {
"version": "0.0.16",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
},
"@vue/compat": {
"version": "3.2.45",
@ -12124,6 +12202,46 @@
}
}
},
"@vueuse/core": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-9.6.0.tgz",
"integrity": "sha512-qGUcjKQXHgN+jqXEgpeZGoxdCbIDCdVPz3QiF1uyecVGbMuM63o96I1GjYx5zskKgRI0FKSNsVWM7rwrRMTf6A==",
"requires": {
"@types/web-bluetooth": "^0.0.16",
"@vueuse/metadata": "9.6.0",
"@vueuse/shared": "9.6.0",
"vue-demi": "*"
},
"dependencies": {
"vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"requires": {}
}
}
},
"@vueuse/metadata": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-9.6.0.tgz",
"integrity": "sha512-sIC8R+kWkIdpi5X2z2Gk8TRYzmczDwHRhEFfCu2P+XW2JdPoXrziqsGpDDsN7ykBx4ilwieS7JUIweVGhvZ93w=="
},
"@vueuse/shared": {
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-9.6.0.tgz",
"integrity": "sha512-/eDchxYYhkHnFyrb00t90UfjCx94kRHxc7J1GtBCqCG4HyPMX+krV9XJgVtWIsAMaxKVU4fC8NSUviG1JkwhUQ==",
"requires": {
"vue-demi": "*"
},
"dependencies": {
"vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"requires": {}
}
}
},
"@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",

View File

@ -17,10 +17,10 @@
"@fullcalendar/luxon2": "^5.10.2",
"@fullcalendar/timegrid": "^5.9.0",
"@fullcalendar/vue3": "^5.11",
"@vue-leaflet/vue-leaflet": "*",
"@vue/compat": "^3.2.45",
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"@vueuse/core": "^9.6.0",
"axios": "^1",
"bootstrap": "^4.6.0 <5",
"bootstrap-notify": "^3.1.3",

View File

@ -0,0 +1,61 @@
<template>
<div id="leaflet-container" ref="map">
<slot v-if="$map" :map="$map"/>
</div>
</template>
<style lang="scss">
@import 'leaflet/dist/leaflet.css';
.leaflet-container {
height: 300px;
z-index: 0;
}
</style>
<script setup>
import {onMounted, provide, ref} from "vue";
import L from "leaflet";
import {get, set, templateRef} from "@vueuse/core";
const props = defineProps({
attribution: String
});
const $container = templateRef('map');
const $map = ref();
provide('map', $map);
onMounted(() => {
// Fix issue with Leaflet icons being built in Webpack
// https://github.com/Leaflet/Leaflet/issues/4968
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});
// Init map
const map = L.map(get($container));
map.setView([40, 0], 1);
set($map, map);
// Add tile layer
const tileUrl = 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/{theme}_all/{z}/{x}/{y}.png';
const tileAttribution = 'Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL.';
L.tileLayer(tileUrl, {
theme: App.theme,
attribution: tileAttribution,
}).addTo(map);
/*
// Add fullscreen control
const fullscreenControl = new L.Control.Fullscreen();
map.addControl(fullscreenControl)
*/
});
</script>

View File

@ -1,63 +1,38 @@
<template>
<l-map v-if="mapPoints.length < 3000" style="height: 300px; z-index: 0;" :zoom="1" :center="[40, 0]">
<l-tile-layer :url="tileUrl" :attribution="tileAttribution"></l-tile-layer>
<l-marker v-for="l in mapPoints" :key="l.hash"
:lat-lng="{lat: l.location.lat, lng: l.location.lon}">
<l-tooltip>
IP: {{ l.ip }}<br>
Country: {{ l.location.country }}<br>
Region: {{ l.location.region }}<br>
City: {{ l.location.city }}<br>
Time connected: {{ l.connected_time }}<br>
User Agent: {{ l.user_agent }}
</l-tooltip>
</l-marker>
</l-map>
<inner-map v-if="visibleListeners.length < 3000"
:attribution="attribution">
<map-point v-for="l in visibleListeners" :key="l.hash"
:position="[l.location.lat, l.location.lon]">
<translate key="l-ip">IP</translate>
: {{ l.ip }}<br>
<translate key="l-country">Country</translate>
: {{ l.location.country }}<br>
<translate key="l-region">Region</translate>
: {{ l.location.region }}<br>
<translate key="l-city">City</translate>
: {{ l.location.city }}<br>
<translate key="l-time">Time</translate>
: {{ l.connected_time }}<br>
<translate key="l-ua">User Agent</translate>
: {{ l.user_agent }}
</map-point>
</inner-map>
</template>
<style lang="css">
@import '../../../../../node_modules/leaflet/dist/leaflet.css';
</style>
<script setup>
import InnerMap from "./InnerMap.vue";
import MapPoint from "./MapPoint.vue";
import {computed} from "vue";
import _ from "lodash";
<script>
import L from 'leaflet';
import {LMap, LMarker, LTileLayer, LTooltip} from '@vue-leaflet/vue-leaflet';
import _ from 'lodash';
const props = defineProps({
attribution: String,
listeners: Array,
});
export default {
name: 'StationReportsListenersMap',
props: {
attribution: String,
listeners: Array,
},
components: {
LMap,
LTileLayer,
LMarker,
LTooltip
},
data() {
return {
tileUrl: 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/' + App.theme + '_all/{z}/{x}/{y}.png',
tileAttribution: 'Map tiles by Carto, under CC BY 3.0. Data by OpenStreetMap, under ODbL.',
}
},
mounted() {
// Fix issue with Leaflet icons being built in Webpack
// https://github.com/Leaflet/Leaflet/issues/4968
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
});
},
computed: {
mapPoints() {
return _.filter(this.listeners, function (l) {
return null !== l.location.lat && null !== l.location.lon;
});
}
}
};
const visibleListeners = computed(() => {
return _.filter(props.listeners, function (l) {
return null !== l.location.lat && null !== l.location.lon;
});
});
</script>

View File

@ -0,0 +1,37 @@
<template>
<div ref="popup-content">
<slot/>
</div>
</template>
<script setup>
import {get, set, templateRef} from '@vueuse/core';
import {inject, onUnmounted, ref, toRaw, watch} from 'vue';
const props = defineProps({
position: Array
});
const $map = inject('map');
const $marker = ref();
const map = toRaw(get($map));
const marker = L.marker(props.position);
marker.addTo(map);
set($marker, marker);
const popup = new L.Popup();
const $popupContent = templateRef('popup-content');
watch(
$popupContent,
(content) => {
popup.setContent(content);
marker.bindPopup(popup);
},
{immediate: true}
);
onUnmounted(() => {
get($marker).remove();
});
</script>

View File

@ -0,0 +1,5 @@
<template>
<slot/>
</template>
<script setup></script>