Enhance Map Zoom Control and Improve Map Page Layout
Some checks failed
build.yaml / Enhance Map Zoom Control and Improve Map Page Layout (push) Failing after 0s
Some checks failed
build.yaml / Enhance Map Zoom Control and Improve Map Page Layout (push) Failing after 0s
- Refactored zoom control component for better accessibility and styling. - Added hover effects and improved button states for zoom in/out buttons. - Updated map page layout with enhanced dataset card design and responsive styles. - Introduced empty state for no datasets found and improved results header. - Added icons for dataset cards and improved author display.
This commit is contained in:
parent
88e37bfee8
commit
4229001572
7 changed files with 1520 additions and 452 deletions
|
|
@ -8,7 +8,6 @@ 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';
|
||||
|
|
@ -17,14 +16,7 @@ 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) {
|
||||
|
|
@ -51,21 +43,18 @@ Map.include({
|
|||
},
|
||||
|
||||
_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,
|
||||
checkable: Boolean,
|
||||
datasets: {
|
||||
type: Array<OpensearchDocument>,
|
||||
default: () => [],
|
||||
|
|
@ -89,10 +78,7 @@ 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);
|
||||
},
|
||||
|
|
@ -103,15 +89,13 @@ const fitBounds: LatLngBoundsExpression = [
|
|||
[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 isLoading = ref(false);
|
||||
|
||||
const filterLayerGroup = new LayerGroup();
|
||||
// Replace with your actual data
|
||||
// const datasets: Ref<OpensearchDocument[]> = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
initMap();
|
||||
|
|
@ -122,7 +106,6 @@ onUnmounted(() => {
|
|||
});
|
||||
|
||||
const initMap = async () => {
|
||||
// init leaflet map
|
||||
map = new Map('map', props.mapOptions);
|
||||
mapService.setMap(props.mapId, map);
|
||||
map.scrollWheelZoom.disable();
|
||||
|
|
@ -140,11 +123,6 @@ const initMap = async () => {
|
|||
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,
|
||||
|
|
@ -153,62 +131,15 @@ const initMap = async () => {
|
|||
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) => {
|
||||
isLoading.value = true;
|
||||
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({
|
||||
|
|
@ -225,7 +156,6 @@ const handleDrawEventCreated = async (event) => {
|
|||
filter: {
|
||||
geo_shape: {
|
||||
geo_location: {
|
||||
// replace 'location' with your geo-point field name
|
||||
shape: {
|
||||
type: 'envelope',
|
||||
coordinates: [
|
||||
|
|
@ -237,16 +167,12 @@ const handleDrawEventCreated = async (event) => {
|
|||
},
|
||||
},
|
||||
},
|
||||
// _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;
|
||||
|
|
@ -255,46 +181,255 @@ const handleDrawEventCreated = async (event) => {
|
|||
[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 });
|
||||
let rect = new Rectangle(bbox, {
|
||||
color: '#65DC21',
|
||||
weight: 2,
|
||||
fillColor: '#65DC21',
|
||||
fillOpacity: 0.2,
|
||||
className: 'animated-rectangle',
|
||||
});
|
||||
filterLayerGroup.addLayer(rect);
|
||||
// add to result list
|
||||
items.value.push(hit._source);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
</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">
|
||||
<SectionMain>
|
||||
<div class="map-container-wrapper">
|
||||
<!-- Loading Overlay -->
|
||||
<div v-if="isLoading" class="loading-overlay">
|
||||
<div class="loading-spinner"></div>
|
||||
<p class="loading-text">Searching datasets...</p>
|
||||
</div>
|
||||
|
||||
<!-- Map Instructions Banner -->
|
||||
<div class="map-instructions">
|
||||
<svg class="instruction-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<path d="M12 16v-4M12 8h.01" />
|
||||
</svg>
|
||||
<p class="instruction-text">
|
||||
<strong>Tip:</strong> Use the drawing tool to select an area on the map and discover datasets
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div id="map" class="map-container">
|
||||
<ZoomControlComponent ref="zoomControl" :mapId="mapId"></ZoomControlComponent>
|
||||
<DrawControlComponent ref="drawControl" :preserve="false" :mapId="mapId" :southWest="southWest"
|
||||
:northEast="northEast">
|
||||
<DrawControlComponent ref="drawControl" :preserve="false" :mapId="mapId" :southWest="southWest" :northEast="northEast">
|
||||
</DrawControlComponent>
|
||||
</div>
|
||||
</SectionMain>
|
||||
</div>
|
||||
</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%;
|
||||
<style scoped>
|
||||
.map-container-wrapper {
|
||||
position: relative;
|
||||
border-radius: 1rem;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
box-shadow:
|
||||
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.leaflet-container .leaflet-pane {
|
||||
z-index: 30!important;
|
||||
.dark .map-container-wrapper {
|
||||
background: #1f2937;
|
||||
}
|
||||
</style>
|
||||
|
||||
/* Map Instructions Banner */
|
||||
.map-instructions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.5rem;
|
||||
background: linear-gradient(135deg, rgba(101, 220, 33, 0.1) 0%, rgba(53, 124, 6, 0.1) 100%);
|
||||
border-bottom: 2px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.dark .map-instructions {
|
||||
background: linear-gradient(135deg, rgba(101, 220, 33, 0.2) 0%, rgba(53, 124, 6, 0.2) 100%);
|
||||
border-bottom-color: #374151;
|
||||
}
|
||||
|
||||
.instruction-icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
color: #65dc21;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.instruction-text {
|
||||
font-size: 0.875rem;
|
||||
color: #4b5563;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dark .instruction-text {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
.instruction-text strong {
|
||||
color: #65dc21;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Loading Overlay */
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.dark .loading-overlay {
|
||||
background: rgba(31, 41, 55, 0.95);
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: 4px solid #e5e7eb;
|
||||
border-top-color: #65dc21;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.dark .loading-spinner {
|
||||
border-color: #374151;
|
||||
border-top-color: #65dc21;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
color: #65dc21;
|
||||
}
|
||||
|
||||
/* Map Container */
|
||||
.map-container {
|
||||
position: relative;
|
||||
height: 600px;
|
||||
width: 100%;
|
||||
background: #f9fafb;
|
||||
}
|
||||
|
||||
.dark .map-container {
|
||||
background: #111827;
|
||||
}
|
||||
|
||||
/* Leaflet Overrides */
|
||||
:deep(.leaflet-container) {
|
||||
height: 600px;
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
:deep(.leaflet-container .leaflet-pane) {
|
||||
z-index: 30 !important;
|
||||
}
|
||||
|
||||
/* Enhanced Rectangle Styling */
|
||||
:deep(.animated-rectangle) {
|
||||
animation: pulseRectangle 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulseRectangle {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Control Enhancements */
|
||||
:deep(.leaflet-control) {
|
||||
border-radius: 0.5rem;
|
||||
box-shadow:
|
||||
0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
border: none;
|
||||
}
|
||||
|
||||
:deep(.leaflet-bar a) {
|
||||
border-radius: 0.5rem;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.leaflet-bar a:hover) {
|
||||
background: #65dc21;
|
||||
color: white;
|
||||
}
|
||||
|
||||
:deep(.leaflet-draw-toolbar a) {
|
||||
background: white;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dark :deep(.leaflet-draw-toolbar a) {
|
||||
background: #374151;
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
:deep(.leaflet-draw-toolbar a:hover) {
|
||||
background: #65dc21;
|
||||
}
|
||||
|
||||
/* Popup Enhancements */
|
||||
:deep(.leaflet-popup-content-wrapper) {
|
||||
border-radius: 0.75rem;
|
||||
box-shadow:
|
||||
0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:deep(.leaflet-popup-tip) {
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.map-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.map-instructions {
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.instruction-text {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.map-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue