You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
213 lines
6.7 KiB
213 lines
6.7 KiB
<template>
|
|
<div class="map-wrap">
|
|
<div id="map" class="map" ref="mapContainer"></div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<script>
|
|
import maplibregl from "maplibre-gl";
|
|
import { markRaw, onMounted, onUnmounted, reactive, shallowRef, watch } from "vue";
|
|
export default {
|
|
name: "map-component",
|
|
props: {
|
|
idlist: {
|
|
type: Array,
|
|
required: true,
|
|
}
|
|
},
|
|
setup(props, context) {
|
|
const mapContainer = shallowRef(null);
|
|
const map = shallowRef(null);
|
|
let currentFadr = reactive({ "fadr": [] });
|
|
|
|
const apiKey = "pk.eyJ1IjoiZ2hlcm1hbnQiLCJhIjoiY2pncDUwcnRmNDQ4ZjJ4czdjZXMzaHZpNyJ9.3rFyYRRtvLUngHm027HZ7A";
|
|
|
|
|
|
watch(()=> [...props.idlist], (_currentValue, _oldValue) => {
|
|
updateSamplesLayer()
|
|
});
|
|
|
|
const buildMap = () => {
|
|
const initialState = { lng: 37.625, lat: 55.751, zoom: 5 };
|
|
|
|
map.value = markRaw(new maplibregl.Map({
|
|
container: mapContainer.value, // container id
|
|
// DOCS: https://maplibre.org/maplibre-gl-js-docs/style-spec/
|
|
style: {
|
|
version: 8,
|
|
sources: {},
|
|
layers: []
|
|
},
|
|
center: [37.625, 55.751], // starting position [lng, lat]
|
|
zoom: 2, // starting zoom
|
|
maxZoom: 10
|
|
}));
|
|
|
|
map.value.on('load', () => {
|
|
map.value.addSource('countries', {
|
|
'type': 'vector',
|
|
"tiles": [`https://api.mapbox.com/v4/ghermant.aq1p7k29/{z}/{x}/{y}.mvt?access_token=${apiKey}`]
|
|
});
|
|
|
|
map.value.addLayer(
|
|
{
|
|
'id': 'countries-layer',
|
|
'type': 'line',
|
|
'source': 'countries',
|
|
'source-layer': 'ne_110m_admin_0_countries-cz6wwp',
|
|
'paint': {
|
|
"line-color": "blue",
|
|
"line-width": 3
|
|
},
|
|
'filter': ["==", ["get", "NAME"], "Russia"],
|
|
"maxzoom": 4
|
|
}
|
|
);
|
|
|
|
|
|
map.value.addSource('samples', {
|
|
'type': 'vector',
|
|
"tiles": ["http://localhost:8080/martin/public.geodata/{z}/{x}/{y}.pbf"],
|
|
'promoteId': 'fadr',
|
|
});
|
|
|
|
map.value.addLayer({
|
|
'id': 'samples-layer',
|
|
'source': 'samples',
|
|
'source-layer': 'public.geodata',
|
|
'type': 'circle',
|
|
'paint': {
|
|
'circle-stroke-width': 1,
|
|
'circle-stroke-color': '#FFFFFF',
|
|
'circle-color': [
|
|
'case',
|
|
['boolean', ['feature-state', 'beenClicked'], false],
|
|
'#ffff00',
|
|
'#1a9641'
|
|
],
|
|
'circle-opacity': 0.8,
|
|
'circle-radius': 16
|
|
},
|
|
filter: ["match", ["get", "internal_id"], props.idlist, true, false]
|
|
});
|
|
});
|
|
|
|
const popup = new maplibregl.Popup({
|
|
closeButton: false,
|
|
closeOnClick: false,
|
|
})
|
|
|
|
map.value.on('mouseover', 'samples-layer', (e) => {
|
|
// Change the cursor style as a UI indicator.
|
|
map.value.getCanvas().style.cursor = 'pointer';
|
|
});
|
|
|
|
map.value.on('mouseleave', 'samples-layer', (e) => {
|
|
map.value.getCanvas().style.cursor = '';
|
|
popup.remove();
|
|
});
|
|
|
|
map.value.on('click', 'samples-layer', (e) => {
|
|
// Populate the popup and set its coordinates
|
|
// based on the feature found.
|
|
popup
|
|
.setLngLat(e.lngLat)
|
|
.setHTML(JSON.stringify(e.features[0].properties))
|
|
.addTo(map.value);
|
|
let newFadr = e.features[0].properties["fadr"];
|
|
|
|
let newFadrFound = currentFadr["fadr"].indexOf(newFadr)
|
|
if (newFadrFound > -1) {
|
|
//remove old paint
|
|
map.value.setFeatureState(
|
|
{
|
|
source: 'samples',
|
|
sourceLayer: 'public.geodata',
|
|
id: newFadr
|
|
},
|
|
{ beenClicked: false }
|
|
);
|
|
|
|
// remove from filters
|
|
currentFadr["fadr"].splice(newFadrFound, 1)
|
|
} else {
|
|
// apply new paint
|
|
map.value.setFeatureState(
|
|
{
|
|
source: 'samples',
|
|
sourceLayer: 'public.geodata',
|
|
id: newFadr
|
|
},
|
|
{ beenClicked: true }
|
|
);
|
|
|
|
// add to filters
|
|
currentFadr["fadr"].push(newFadr);
|
|
}
|
|
|
|
context.emit('mapClick', currentFadr);
|
|
});
|
|
|
|
};
|
|
|
|
const updateSamplesLayer = () => {
|
|
map.value.removeLayer('samples-layer');
|
|
|
|
map.value.addLayer({
|
|
'id': 'samples-layer',
|
|
'source': 'samples',
|
|
'source-layer': 'public.geodata',
|
|
'type': 'circle',
|
|
'paint': {
|
|
'circle-stroke-width': 1,
|
|
'circle-stroke-color': '#FFFFFF',
|
|
'circle-color': [
|
|
'case',
|
|
['boolean', ['feature-state', 'beenClicked'], false],
|
|
'#ffff00',
|
|
'#1a9641'
|
|
],
|
|
'circle-opacity': 0.8,
|
|
'circle-radius': 16
|
|
},
|
|
filter: ["match", ["get", "internal_id"], props.idlist, true, false]
|
|
});
|
|
|
|
};
|
|
|
|
onMounted(() => {
|
|
buildMap()
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
// TODO: remove even listeners too
|
|
map.value?.remove();
|
|
})
|
|
|
|
|
|
return {
|
|
map, mapContainer, currentFadr
|
|
}
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
<style lang="css">
|
|
.map-wrap {
|
|
position: relative;
|
|
height: 100vh;
|
|
width: 100%;
|
|
}
|
|
|
|
.map {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
/* z-index: -1; */
|
|
}
|
|
</style> |