mirror of
https://github.com/IT4Change/gradido.git
synced 2026-02-06 01:46:07 +00:00
233 lines
5.8 KiB
Vue
233 lines
5.8 KiB
Vue
<template>
|
|
<div>
|
|
<coordinates-display
|
|
v-if="map"
|
|
:community-position="communityPosition"
|
|
:user-position="userPosition"
|
|
@centerMap="handleMapCenter"
|
|
/>
|
|
<div ref="mapContainer" class="map-container" />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
|
import L from 'leaflet'
|
|
import { GeoSearchControl, OpenStreetMapProvider } from 'leaflet-geosearch'
|
|
import 'leaflet-geosearch/dist/geosearch.css'
|
|
import CoordinatesDisplay from '@/components/UserSettings/CoordinatesDisplay.vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
const mapContainer = ref(null)
|
|
const map = ref(null)
|
|
const userMarker = ref(null)
|
|
const communityMarker = ref(null)
|
|
const userPosition = ref({ lat: 0, lng: 0 })
|
|
const communityPosition = ref({ lat: 0, lng: 0 })
|
|
const defaultZoom = 13
|
|
|
|
const emit = defineEmits(['update:userPosition'])
|
|
|
|
const props = defineProps({
|
|
userMarkerCoords: Object,
|
|
communityMarkerCoords: Object,
|
|
})
|
|
|
|
const { t } = useI18n()
|
|
|
|
onMounted(async () => {
|
|
if (props.userMarkerCoords) {
|
|
userPosition.value = props.userMarkerCoords
|
|
}
|
|
if (props.communityMarkerCoords) {
|
|
communityPosition.value = props.communityMarkerCoords
|
|
}
|
|
setTimeout(() => initMap(), 250)
|
|
window.addEventListener('resize', handleResize)
|
|
})
|
|
|
|
onUnmounted(() => {
|
|
if (map.value) {
|
|
map.value.remove()
|
|
}
|
|
window.removeEventListener('resize', handleResize)
|
|
})
|
|
|
|
function initMap() {
|
|
if (mapContainer.value && !map.value) {
|
|
map.value = L.map(mapContainer.value, {
|
|
center: [userPosition.value.lat, userPosition.value.lng],
|
|
zoom: defaultZoom,
|
|
zoomControl: false,
|
|
closePopupOnClick: false,
|
|
})
|
|
|
|
L.control.zoom({ position: 'topleft' }).addTo(map.value)
|
|
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
attribution: '© OpenStreetMap contributors',
|
|
maxZoom: 19,
|
|
}).addTo(map.value)
|
|
|
|
// User marker (movable)
|
|
userMarker.value = L.marker([userPosition.value.lat, userPosition.value.lng], {
|
|
draggable: true,
|
|
interactive: false,
|
|
icon: L.icon({
|
|
iconUrl:
|
|
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-red.png',
|
|
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
|
iconSize: [25, 41],
|
|
iconAnchor: [12, 41],
|
|
popupAnchor: [1, -34],
|
|
shadowSize: [41, 41],
|
|
}),
|
|
}).addTo(map.value)
|
|
|
|
userMarker.value
|
|
.bindPopup(t('settings.GMS.map.userLocationLabel'), {
|
|
autoClose: false,
|
|
closeOnClick: false,
|
|
closeButton: false,
|
|
})
|
|
.openPopup()
|
|
|
|
// Community marker (fixed)
|
|
communityMarker.value = L.marker([communityPosition.value.lat, communityPosition.value.lng], {
|
|
draggable: false,
|
|
interactive: false,
|
|
icon: L.icon({
|
|
iconUrl:
|
|
'https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-blue.png',
|
|
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
|
|
iconSize: [25, 41],
|
|
iconAnchor: [12, 41],
|
|
popupAnchor: [1, -34],
|
|
shadowSize: [41, 41],
|
|
}),
|
|
}).addTo(map.value)
|
|
|
|
communityMarker.value
|
|
.bindPopup(t('settings.GMS.map.communityLocationLabel'), {
|
|
autoClose: false,
|
|
closeOnClick: false,
|
|
closeButton: false,
|
|
})
|
|
.openPopup()
|
|
|
|
map.value.on('click', onMapClick)
|
|
userMarker.value.on('dragend', onMarkerDragEnd)
|
|
|
|
// GeoSearch control
|
|
const provider = new OpenStreetMapProvider()
|
|
const searchControl = new GeoSearchControl({
|
|
provider: provider,
|
|
style: 'button',
|
|
showMarker: false,
|
|
showPopup: false,
|
|
autoClose: true,
|
|
retainZoomLevel: false,
|
|
animateZoom: true,
|
|
keepResult: false,
|
|
searchLabel: t('settings.GMS.map.search'),
|
|
})
|
|
map.value.addControl(searchControl)
|
|
|
|
map.value.on('geosearch/showlocation', (result) => {
|
|
const { x, y, label } = result.location
|
|
updateUserPosition({ lat: y, lng: x })
|
|
})
|
|
|
|
// Center map on user position
|
|
centerMapOnUser()
|
|
}
|
|
}
|
|
|
|
function handleResize() {
|
|
if (map.value) {
|
|
map.value.invalidateSize()
|
|
centerMapOnUser()
|
|
}
|
|
}
|
|
|
|
function onMapClick(e) {
|
|
updateUserPosition(e.latlng)
|
|
}
|
|
|
|
function onMarkerDragEnd() {
|
|
if (userMarker.value) {
|
|
updateUserPosition(userMarker.value.getLatLng())
|
|
}
|
|
}
|
|
|
|
function updateUserPosition(latlng) {
|
|
userPosition.value = { lat: latlng.lat, lng: latlng.lng }
|
|
if (userMarker.value) {
|
|
userMarker.value.setLatLng(latlng)
|
|
userMarker.value.openPopup()
|
|
}
|
|
centerMapOnUser()
|
|
emit('update:userPosition', userPosition.value)
|
|
}
|
|
|
|
function centerMapOnUser() {
|
|
if (map.value && userPosition.value) {
|
|
map.value.setView([userPosition.value.lat, userPosition.value.lng], map.value.getZoom(), {
|
|
animate: true,
|
|
pan: {
|
|
duration: 0.5,
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
function centerMapOnCommunity() {
|
|
if (map.value && communityPosition.value) {
|
|
map.value.setView(
|
|
[communityPosition.value.lat, communityPosition.value.lng],
|
|
map.value.getZoom(),
|
|
{
|
|
animate: true,
|
|
pan: {
|
|
duration: 0.5,
|
|
},
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
function handleMapCenter(centerMode) {
|
|
if (centerMode === 'USER') centerMapOnUser()
|
|
else centerMapOnCommunity()
|
|
}
|
|
|
|
watch(userPosition, (newPosition) => {
|
|
emit('update:userPosition', newPosition)
|
|
})
|
|
</script>
|
|
|
|
<style scoped>
|
|
.map-container {
|
|
height: 400px;
|
|
width: 100%;
|
|
}
|
|
|
|
.leaflet-control-custom a {
|
|
background-color: #fff;
|
|
width: 30px;
|
|
height: 30px;
|
|
line-height: 30px;
|
|
text-align: center;
|
|
text-decoration: none;
|
|
color: black;
|
|
}
|
|
|
|
.leaflet-control-custom a:hover {
|
|
background-color: #f4f4f4;
|
|
}
|
|
|
|
:deep(.leaflet-control-zoom > a) {
|
|
color: #555 !important;
|
|
}
|
|
</style>
|