- Creazione "AbitareGliIblei"
- Mappa Interattiva con i markers
This commit is contained in:
3
src/components/CMapGetCoordinates/CMapGetCoordinates.scss
Executable file
3
src/components/CMapGetCoordinates/CMapGetCoordinates.scss
Executable file
@@ -0,0 +1,3 @@
|
||||
.map-container {
|
||||
height: 400px;
|
||||
}
|
||||
170
src/components/CMapGetCoordinates/CMapGetCoordinates.ts
Executable file
170
src/components/CMapGetCoordinates/CMapGetCoordinates.ts
Executable file
@@ -0,0 +1,170 @@
|
||||
import { tools } from '@store/Modules/tools'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { defineComponent, onMounted, ref, watch, computed } from 'vue'
|
||||
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import * as L from 'leaflet'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.css'
|
||||
import 'leaflet.markercluster/dist/MarkerCluster.Default.css'
|
||||
import 'leaflet.markercluster'
|
||||
|
||||
import { useUserStore } from '@src/store/UserStore'
|
||||
|
||||
// Importa le immagini dei marker
|
||||
import icon from 'leaflet/dist/images/marker-icon.png'
|
||||
import iconShadow from 'leaflet/dist/images/marker-shadow.png'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CMapGetCoordinates',
|
||||
setup(props, { emit }) {
|
||||
const $q = useQuasar()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const iconWidth = ref(25)
|
||||
const iconHeight = ref(41)
|
||||
const map = ref<L.Map | null>(null)
|
||||
const marker = ref<L.Marker | null>(null)
|
||||
const address = ref('')
|
||||
const suggestions = ref([]);
|
||||
const coordinates = ref(<[number, number]>[0, 0])
|
||||
const isMapDialogOpen = ref(false)
|
||||
|
||||
const zoomLevel = ref(17)
|
||||
|
||||
const myheight = computed(() => {
|
||||
return $q.screen.height - 150
|
||||
})
|
||||
|
||||
const initMap = () => {
|
||||
// Configura l'icona di default
|
||||
const DefaultIcon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow,
|
||||
iconSize: [iconWidth.value, iconHeight.value],
|
||||
iconAnchor: [iconWidth.value / 2, iconHeight.value],
|
||||
popupAnchor: [1, -iconHeight.value],
|
||||
shadowSize: [iconHeight.value, iconHeight.value]
|
||||
})
|
||||
L.Marker.prototype.options.icon = DefaultIcon
|
||||
|
||||
map.value = L.map('map',
|
||||
{
|
||||
zoomControl: true, zoom: zoomLevel.value, zoomAnimation: true,
|
||||
fadeAnimation: true, markerZoomAnimation: true,
|
||||
center: [42.71, 12.934],
|
||||
}
|
||||
)
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map.value)
|
||||
|
||||
const closeButton = L.control({ position: 'topright' });
|
||||
|
||||
closeButton.onAdd = function () {
|
||||
const button = L.DomUtil.create('button', 'leaflet-control-button');
|
||||
button.innerHTML = 'Chiudi'; // Testo del pulsante
|
||||
button.style.backgroundColor = 'red';
|
||||
button.style.color = 'white';
|
||||
button.style.border = 'none';
|
||||
button.style.borderRadius = '5px';
|
||||
button.style.padding = '10px';
|
||||
button.style.cursor = 'pointer';
|
||||
|
||||
button.onclick = () => {
|
||||
isMapDialogOpen.value = false; // Chiudi il dialog della mappa
|
||||
};
|
||||
|
||||
return button;
|
||||
};
|
||||
|
||||
closeButton.addTo(map.value);
|
||||
|
||||
}
|
||||
|
||||
const updateMarker = (lat: number, lng: number) => {
|
||||
console.log('aggiorna marker', lat, lng)
|
||||
if (marker.value) {
|
||||
marker.value.setLatLng([lat, lng])
|
||||
} else {
|
||||
marker.value = L.marker([lat, lng], { draggable: true }).addTo(map.value!)
|
||||
marker.value.on('dragend', (event: L.LeafletEvent) => {
|
||||
const position = (event.target as L.Marker).getLatLng()
|
||||
coordinates.value = [
|
||||
parseFloat(position.lng.toFixed(6)),
|
||||
parseFloat(position.lat.toFixed(6)),
|
||||
]
|
||||
|
||||
emit('update:coordinates', coordinates.value)
|
||||
})
|
||||
}
|
||||
map.value?.setView([lat, lng], 17)
|
||||
}
|
||||
|
||||
const searchAddress = async () => {
|
||||
try {
|
||||
const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address.value)}`)
|
||||
const data = await response.json()
|
||||
if (data && data.length > 0) {
|
||||
coordinates.value = [
|
||||
parseFloat(data[0].lon),
|
||||
parseFloat(data[0].lat)
|
||||
]
|
||||
emit('update:coordinates', coordinates.value)
|
||||
isMapDialogOpen.value = true
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error searching address:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const getAddressSuggestions = async (val: string, update: Function) => {
|
||||
try {
|
||||
if (val.length === 0) {
|
||||
suggestions.value = [];
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(val)}`
|
||||
);
|
||||
const data = await response.json();
|
||||
suggestions.value = data.map((item: any) => ({
|
||||
label: item.display_name,
|
||||
value: item.display_name,
|
||||
lat: item.lat,
|
||||
lon: item.lon
|
||||
}));
|
||||
|
||||
update();
|
||||
} catch (error) {
|
||||
console.error('Error fetching address suggestions:', error);
|
||||
}
|
||||
};
|
||||
|
||||
watch(address, (newAddress) => {
|
||||
const match: any = suggestions.value.find((suggestion: any) => suggestion.value === newAddress.value);
|
||||
if (match && match.lat) {
|
||||
coordinates.value = [
|
||||
parseFloat(parseFloat(match.lon).toFixed(6)),
|
||||
parseFloat(parseFloat(match.lat).toFixed(6))
|
||||
]
|
||||
};
|
||||
|
||||
updateMarker(coordinates.value[1], coordinates.value[0]);
|
||||
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
initMap()
|
||||
})
|
||||
|
||||
return {
|
||||
address,
|
||||
coordinates,
|
||||
searchAddress,
|
||||
myheight,
|
||||
isMapDialogOpen,
|
||||
suggestions,
|
||||
getAddressSuggestions,
|
||||
}
|
||||
}
|
||||
})
|
||||
57
src/components/CMapGetCoordinates/CMapGetCoordinates.vue
Executable file
57
src/components/CMapGetCoordinates/CMapGetCoordinates.vue
Executable file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div class="q-pa-sm">
|
||||
<div class="q-gutter-sm">
|
||||
<div class="row q-col-gutter-sm q-mb-sm items-center no-wrap">
|
||||
<div class="col-grow">
|
||||
<q-select
|
||||
v-model="address"
|
||||
label="Indirizzo"
|
||||
use-input
|
||||
input-class="col-auto"
|
||||
input-debounce="300"
|
||||
:options="suggestions"
|
||||
@filter="getAddressSuggestions"
|
||||
filled
|
||||
dense
|
||||
hide-dropdown-icon
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-btn @click="searchAddress" label="Cerca" color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<q-col cols="6">
|
||||
<q-input
|
||||
v-model="coordinates.lat" label="Latitudine:"
|
||||
filled dense />
|
||||
</q-col>
|
||||
<q-col cols="6">
|
||||
<q-input
|
||||
v-model="coordinates.lng"
|
||||
label="Longitudine:"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
</q-col>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
id="map"
|
||||
:style="`height:${myheight}px; width:99%`"
|
||||
>
|
||||
<p v-if="coordinates && coordinates.lat">
|
||||
Coordinate: {{ coordinates.lat.toFixed(6) }},
|
||||
{{ coordinates.lng.toFixed(6) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" src="./CMapGetCoordinates.ts">
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './CMapGetCoordinates.scss';
|
||||
</style>
|
||||
1
src/components/CMapGetCoordinates/index.ts
Executable file
1
src/components/CMapGetCoordinates/index.ts
Executable file
@@ -0,0 +1 @@
|
||||
export {default as CMapGetCoordinates} from './CMapGetCoordinates.vue'
|
||||
Reference in New Issue
Block a user