Update package.json to bump @types/leaflet Define leaflet map z-index directly in the main CSS via apps.cc for consistent component use Scope all SearchMap.vue styles locally
300 lines
No EOL
10 KiB
Vue
300 lines
No EOL
10 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted, onUnmounted, ref, Ref, defineProps, computed } from 'vue';
|
|
import SectionMain from '@/Components/SectionMain.vue';
|
|
import { Map } from 'leaflet/src/map/index';
|
|
import { Rectangle } from 'leaflet';
|
|
import { canvas } from 'leaflet/src/layer/vector/Canvas';
|
|
import { svg } from 'leaflet/src/layer/vector/SVG';
|
|
import axios from 'axios';
|
|
import { LatLngBoundsExpression } from 'leaflet/src/geo/LatLngBounds';
|
|
import { tileLayerWMS } from 'leaflet/src/layer/tile/TileLayer.WMS';
|
|
// import { TileLayer } from 'leaflet/src/layer/tile/TileLayer';
|
|
import { Attribution } from 'leaflet/src/control/Control.Attribution';
|
|
import DrawControlComponent from '@/Components/Map/draw.component.vue';
|
|
import ZoomControlComponent from '@/Components/Map/zoom.component.vue';
|
|
import { MapService } from '@/Stores/map.service';
|
|
import { LayerGroup } from 'leaflet/src/layer/LayerGroup';
|
|
import { OpensearchDocument } from '@/Dataset';
|
|
|
|
Map.include({
|
|
// @namespace Map; @method getRenderer(layer: Path): Renderer
|
|
// Returns the instance of `Renderer` that should be used to render the given
|
|
// `Path`. It will ensure that the `renderer` options of the map and paths
|
|
// are respected, and that the renderers do exist on the map.
|
|
getRenderer: function (layer) {
|
|
// @namespace Path; @option renderer: Renderer
|
|
// Use this specific instance of `Renderer` for this path. Takes
|
|
// precedence over the map's [default renderer](#map-renderer).
|
|
var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
|
|
|
|
if (!renderer) {
|
|
renderer = this._renderer = this._createRenderer();
|
|
}
|
|
|
|
if (!this.hasLayer(renderer)) {
|
|
this.addLayer(renderer);
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_getPaneRenderer: function (name) {
|
|
if (name === 'overlayPane' || name === undefined) {
|
|
return false;
|
|
}
|
|
|
|
var renderer = this._paneRenderers[name];
|
|
if (renderer === undefined) {
|
|
renderer = this._createRenderer({ pane: name });
|
|
this._paneRenderers[name] = renderer;
|
|
}
|
|
return renderer;
|
|
},
|
|
|
|
_createRenderer: function (options) {
|
|
// @namespace Map; @option preferCanvas: Boolean = false
|
|
// Whether `Path`s should be rendered on a `Canvas` renderer.
|
|
// By default, all `Path`s are rendered in a `SVG` renderer.
|
|
return (this.options.preferCanvas && canvas(options)) || svg(options);
|
|
},
|
|
});
|
|
const DEFAULT_BASE_LAYER_NAME = 'BaseLayer';
|
|
const DEFAULT_BASE_LAYER_ATTRIBUTION = '© <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors';
|
|
const OPENSEARCH_HOST = 'https://catalog.geosphere.at';
|
|
// const OPENSEARCH_HOST = `${process.env.OPENSEARCH_HOST}`;
|
|
// const OPENSEARCH_HOST = `http://${process.env.OPENSEARCH_PUBLIC_HOST}`;
|
|
let map: Map;
|
|
|
|
const props = defineProps({
|
|
dheckable: Boolean,
|
|
datasets: {
|
|
type: Array<OpensearchDocument>,
|
|
default: () => [],
|
|
},
|
|
mapId: {
|
|
type: String,
|
|
default: 'map',
|
|
},
|
|
mapOptions: {
|
|
type: Object,
|
|
default: () => ({
|
|
center: [48.208174, 16.373819],
|
|
zoom: 3,
|
|
zoomControl: false,
|
|
attributionControl: false,
|
|
}),
|
|
},
|
|
});
|
|
|
|
const items = computed({
|
|
get() {
|
|
return props.datasets;
|
|
},
|
|
// setter
|
|
set(value) {
|
|
// Note: we are using destructuring assignment syntax here.
|
|
|
|
props.datasets.length = 0;
|
|
props.datasets.push(...value);
|
|
},
|
|
});
|
|
|
|
const fitBounds: LatLngBoundsExpression = [
|
|
[46.4318173285, 9.47996951665],
|
|
[49.0390742051, 16.9796667823],
|
|
];
|
|
|
|
// const mapId = 'map';
|
|
const drawControl: Ref<DrawControlComponent | null> = ref(null);
|
|
const southWest = ref(null);
|
|
const northEast = ref(null);
|
|
const mapService = MapService();
|
|
|
|
const filterLayerGroup = new LayerGroup();
|
|
// Replace with your actual data
|
|
// const datasets: Ref<OpensearchDocument[]> = ref([]);
|
|
|
|
onMounted(() => {
|
|
initMap();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
map.off('zoomend zoomlevelschange');
|
|
});
|
|
|
|
const initMap = async () => {
|
|
// init leaflet map
|
|
map = new Map('map', props.mapOptions);
|
|
mapService.setMap(props.mapId, map);
|
|
map.scrollWheelZoom.disable();
|
|
map.fitBounds(fitBounds);
|
|
drawControl.value?.toggleDraw();
|
|
|
|
map.addLayer(filterLayerGroup);
|
|
|
|
const attributionControl = new Attribution().addTo(map);
|
|
attributionControl.setPrefix(false);
|
|
|
|
let osmGgray = tileLayerWMS('https://ows.terrestris.de/osm-gray/service', {
|
|
format: 'image/png',
|
|
attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
|
layers: 'OSM-WMS',
|
|
});
|
|
|
|
// let baseAt = new TileLayer('https://{s}.wien.gv.at/basemap/bmapgrau/normal/google3857/{z}/{y}/{x}.png', {
|
|
// subdomains: ['maps', 'maps1', 'maps2', 'maps3', 'maps4'],
|
|
// attribution: DEFAULT_BASE_LAYER_ATTRIBUTION,
|
|
// });
|
|
|
|
let layerOptions = {
|
|
label: DEFAULT_BASE_LAYER_NAME,
|
|
visible: true,
|
|
layer: osmGgray,
|
|
};
|
|
layerOptions.layer.addTo(map);
|
|
|
|
map.on('Draw.Event.CREATED', handleDrawEventCreated);
|
|
|
|
// // const query = {
|
|
// // query: {
|
|
// // term: {
|
|
// // id: "103"
|
|
// // }
|
|
// // }
|
|
// // };
|
|
// // to do : call extra method:
|
|
// const query = {
|
|
// // q: 'id:103'
|
|
// // q: 'author:"Iglseder, Christoph" OR title:"Datensatz"',
|
|
// // q: 'author:"Iglseder"',
|
|
// q: '*',
|
|
// _source: 'author,bbox_xmin,bbox_xmax,bbox_ymin,bbox_ymax,abstract,title',
|
|
// size: 1000
|
|
// // qf:"title^3 author^2 subject^1",
|
|
// }
|
|
// try {
|
|
// let response = await axios({
|
|
// method: 'GET',
|
|
// url: OPEN_SEARCH_HOST + '/tethys-records/_search',
|
|
// headers: { 'Content-Type': 'application/json' },
|
|
// params: query
|
|
// });
|
|
// // Loop through the hits in the response
|
|
// response.data.hits.hits.forEach(hit => {
|
|
// // Get the geo_location attribute
|
|
// // var geo_location = hit._source.geo_location;
|
|
// let xMin = hit._source.bbox_xmin;
|
|
// let xMax = hit._source.bbox_xmax;
|
|
// let yMin = hit._source.bbox_ymin;
|
|
// let yMax = hit._source.bbox_ymax;
|
|
// var bbox: LatLngBoundsExpression = [[yMin, xMin], [yMax, xMax]];
|
|
// // Parse the WKT string to get the bounding box coordinates
|
|
// // var bbox = wktToBbox(geo_location);
|
|
|
|
// // // Add the bounding box to the map as a rectangle
|
|
// new Rectangle(bbox, { color: "#ff7800", weight: 1 }).addTo(map);
|
|
// // console.log(hit._source);
|
|
// });
|
|
// } catch (error) {
|
|
// console.error(error);
|
|
// }
|
|
};
|
|
|
|
const handleDrawEventCreated = async (event) => {
|
|
filterLayerGroup.clearLayers();
|
|
items.value = [];
|
|
|
|
let layer = event.layer;
|
|
let bounds = layer.getBounds();
|
|
// coverage.x_min = bounds.getSouthWest().lng;
|
|
// coverage.y_min = bounds.getSouthWest().lat;
|
|
// coverage.x_max = bounds.getNorthEast().lng;
|
|
// coverage.y_max = bounds.getNorthEast().lat;
|
|
|
|
try {
|
|
let response = await axios({
|
|
method: 'POST',
|
|
url: OPENSEARCH_HOST + '/tethys-records/_search',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
data: {
|
|
size: 1000,
|
|
query: {
|
|
bool: {
|
|
must: {
|
|
match_all: {},
|
|
},
|
|
filter: {
|
|
geo_shape: {
|
|
geo_location: {
|
|
// replace 'location' with your geo-point field name
|
|
shape: {
|
|
type: 'envelope',
|
|
coordinates: [
|
|
[bounds.getSouthWest().lng, bounds.getNorthEast().lat],
|
|
[bounds.getNorthEast().lng, bounds.getSouthWest().lat],
|
|
],
|
|
},
|
|
relation: 'within',
|
|
},
|
|
},
|
|
},
|
|
// _source: 'author,bbox_xmin,bbox_xmax,bbox_ymin,bbox_ymax,abstract,title',
|
|
// "size": 1000
|
|
},
|
|
},
|
|
},
|
|
});
|
|
// Loop through the hits in the response
|
|
response.data.hits.hits.forEach((hit) => {
|
|
// Get the geo_location attribute
|
|
// var geo_location = hit._source.geo_location;
|
|
let xMin = hit._source.bbox_xmin;
|
|
let xMax = hit._source.bbox_xmax;
|
|
let yMin = hit._source.bbox_ymin;
|
|
let yMax = hit._source.bbox_ymax;
|
|
var bbox: LatLngBoundsExpression = [
|
|
[yMin, xMin],
|
|
[yMax, xMax],
|
|
];
|
|
// Parse the WKT string to get the bounding box coordinates
|
|
// var bbox = wktToBbox(geo_location);
|
|
|
|
// // Add the bounding box to the map as a rectangle
|
|
let rect = new Rectangle(bbox, { color: '#ff7800', weight: 1 });
|
|
filterLayerGroup.addLayer(rect);
|
|
// add to result list
|
|
items.value.push(hit._source);
|
|
});
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<SectionMain>
|
|
<div id="map" class="map-container mt-6 mb-6 rounded-2xl py-12 px-6 text-center dark:bg-slate-900 bg-white">
|
|
<ZoomControlComponent ref="zoomControl" :mapId="mapId"></ZoomControlComponent>
|
|
<DrawControlComponent ref="drawControl" :preserve="false" :mapId="mapId" :southWest="southWest"
|
|
:northEast="northEast">
|
|
</DrawControlComponent>
|
|
</div>
|
|
</SectionMain>
|
|
</template>
|
|
|
|
|
|
<style scoped lang="css">
|
|
/* .leaflet-container {
|
|
height: 600px;
|
|
width: 100%;
|
|
background-color: transparent;
|
|
outline-offset: 1px;
|
|
} */
|
|
.leaflet-container {
|
|
height: 600px;
|
|
width: 100%;
|
|
}
|
|
|
|
.leaflet-container .leaflet-pane {
|
|
z-index: 30!important;
|
|
}
|
|
</style> |