From 2dc9643e7380dbd68a388ab5023e747acd76785f Mon Sep 17 00:00:00 2001 From: Andrea Aime Date: Sun, 28 Jul 2024 18:39:14 +0200 Subject: [PATCH] Show vector tiles attributes on hover --- .../main/java/org/geowebcache/demo/Demo.java | 12 ++ .../org/geowebcache/rest/webresources/demo.js | 162 +++++++++++++----- 2 files changed, 130 insertions(+), 44 deletions(-) diff --git a/geowebcache/core/src/main/java/org/geowebcache/demo/Demo.java b/geowebcache/core/src/main/java/org/geowebcache/demo/Demo.java index 5faae2d8e..3d4f9dc95 100644 --- a/geowebcache/core/src/main/java/org/geowebcache/demo/Demo.java +++ b/geowebcache/core/src/main/java/org/geowebcache/demo/Demo.java @@ -307,6 +307,10 @@ private static String generateHTML(TileLayer layer, String gridSetStr, String fo + "#info iframe { width: 100%; height: 250px; border: none; }\n" + ".ol-scale-value {top: 24px; right: 8px; position: absolute; }\n" + ".ol-zoom-value {top: 40px; right: 8px; position: absolute; }\n" + + ".tooltip {position: absolute; background-color: white; border: 1px solid black; padding: 5px; border-radius: 3px; white-space: nowrap; max-height: 200px; overflow-y: auto; display: none;}\n" + + ".tooltip-header {display: flex; justify-content: space-between; align-items: center; padding: 5px; border-bottom: 1pxsolidblack; }\n" + + ".tooltip-content {padding: 5px; max-height: 150px; overflow-y: auto;}\n" + + ".close-button {cursor: pointer; background: none; border: none; font-size: 16px; font-weight: bold; }\n" + "\n"); buf.append("\n"); @@ -320,6 +324,14 @@ private static String generateHTML(TileLayer layer, String gridSetStr, String fo .append("\n"); buf.append("
\n" + "
\n"); + buf.append( + "
\n" + + "
\n" + + " Attributes\n" + + " \n" + + "
\n" + + "
\n" + + "
"); // add parameters in hidden inputs makeHiddenInput(buf, "dpi", Double.toString(gridSubset.getDotsPerInch())); diff --git a/geowebcache/core/src/main/resources/org/geowebcache/rest/webresources/demo.js b/geowebcache/core/src/main/resources/org/geowebcache/rest/webresources/demo.js index 0a675b263..5d86df45c 100644 --- a/geowebcache/core/src/main/resources/org/geowebcache/rest/webresources/demo.js +++ b/geowebcache/core/src/main/resources/org/geowebcache/rest/webresources/demo.js @@ -208,58 +208,132 @@ window.onload = function() { map.updateSize(); } - map.on('singleclick', function(evt) { - document.getElementById('info').innerHTML = ''; + var tooltip = document.getElementById('tooltip'); + var tooltipContent = document.getElementById('tooltip-content'); + var closeButton = document.getElementById('close-button'); - var source = layer.getSource(); - var resolution = view.getResolution(); - var tilegrid = source.getTileGrid(); - var tileResolutions = tilegrid.getResolutions(); - var zoomIdx, diff = Infinity; - - for (var i = 0; i < tileResolutions.length; i++) { - var tileResolution = tileResolutions[i]; - var diffP = Math.abs(resolution-tileResolution); - if (diffP < diff) { - diff = diffP; - zoomIdx = i; + if (getValue('isVector') != 'true') { + map.on('singleclick', function(evt) { + document.getElementById('info').innerHTML = ''; + + var source = layer.getSource(); + var resolution = view.getResolution(); + var tilegrid = source.getTileGrid(); + var tileResolutions = tilegrid.getResolutions(); + var zoomIdx, diff = Infinity; + + for (var i = 0; i < tileResolutions.length; i++) { + var tileResolution = tileResolutions[i]; + var diffP = Math.abs(resolution-tileResolution); + if (diffP < diff) { + diff = diffP; + zoomIdx = i; + } + if (tileResolution < resolution) { + break; + } } - if (tileResolution < resolution) { - break; + var tileSize = tilegrid.getTileSize(zoomIdx); + var tileOrigin = tilegrid.getOrigin(zoomIdx); + + var fx = (evt.coordinate[0] - tileOrigin[0]) / (resolution * tileSize[0]); + var fy = (tileOrigin[1] - evt.coordinate[1]) / (resolution * tileSize[1]); + var tileCol = Math.floor(fx); + var tileRow = Math.floor(fy); + var tileI = Math.floor((fx - tileCol) * tileSize[0]); + var tileJ = Math.floor((fy - tileRow) * tileSize[1]); + var matrixIds = tilegrid.getMatrixIds()[zoomIdx]; + var matrixSet = source.getMatrixSet(); + + var url = baseUrl+'?' + for (var param in params) { + if (param.toUpperCase() == 'TILEMATRIX') { + url = url + 'TILEMATRIX='+matrixIds+'&'; + } else { + url = url + param + '=' + params[param] + '&'; + } } + + url = url + + 'SERVICE=WMTS&REQUEST=GetFeatureInfo' + + '&INFOFORMAT=' + infoFormat + + '&TileCol=' + tileCol + + '&TileRow=' + tileRow + + '&I=' + tileI + + '&J=' + tileJ; + document.getElementById('info').innerHTML = + ''; + }); + } else { + var isPinned = false; + + function updateTooltipContent(properties) { + tooltipContent.innerHTML = Object.keys(properties).map(function(key) { + return key + ': ' + properties[key]; + }).join('
'); } - var tileSize = tilegrid.getTileSize(zoomIdx); - var tileOrigin = tilegrid.getOrigin(zoomIdx); + + map.on('pointermove', function(evt) { + if (isPinned) { + return; // Do nothing if the tooltip is pinned + } - var fx = (evt.coordinate[0] - tileOrigin[0]) / (resolution * tileSize[0]); - var fy = (tileOrigin[1] - evt.coordinate[1]) / (resolution * tileSize[1]); - var tileCol = Math.floor(fx); - var tileRow = Math.floor(fy); - var tileI = Math.floor((fx - tileCol) * tileSize[0]); - var tileJ = Math.floor((fy - tileRow) * tileSize[1]); - var matrixIds = tilegrid.getMatrixIds()[zoomIdx]; - var matrixSet = source.getMatrixSet(); + var pixel = evt.pixel; + var feature = map.forEachFeatureAtPixel(pixel, function(feature) { + return feature; + }); - var url = baseUrl+'?' - for (var param in params) { - if (param.toUpperCase() == 'TILEMATRIX') { - url = url + 'TILEMATRIX='+matrixIds+'&'; - } else { - url = url + param + '=' + params[param] + '&'; - } - } + if (feature) { + var coordinates = evt.coordinate; + var properties = feature.getProperties(); - url = url - + 'SERVICE=WMTS&REQUEST=GetFeatureInfo' - + '&INFOFORMAT=' + infoFormat - + '&TileCol=' + tileCol - + '&TileRow=' + tileRow - + '&I=' + tileI - + '&J=' + tileJ; - document.getElementById('info').innerHTML = - ''; - }); + // Display the feature properties in the tooltip + updateTooltipContent(properties); + + tooltip.style.left = (evt.originalEvent.clientX + 10) + 'px'; + tooltip.style.top = (evt.originalEvent.clientY + 10) + 'px'; + tooltip.style.display = 'block'; + } else { + tooltip.style.display = 'none'; + } + }); + + map.on('singleclick', function(evt) { + if (isPinned) { + // Unpin the tooltip if it's already pinned + isPinned = false; + tooltip.style.display = 'none'; + tooltipContent.scrollTop = 0; // Reset scroll position to top + } else { + // Pin the tooltip + var pixel = evt.pixel; + var feature = map.forEachFeatureAtPixel(pixel, function(feature) { + return feature; + }); + + if (feature) { + var coordinates = evt.coordinate; + var properties = feature.getProperties(); + + // Display the feature properties in the tooltip + updateTooltipContent(properties); + tooltip.style.left = (evt.originalEvent.clientX + 10) + 'px'; + tooltip.style.top = (evt.originalEvent.clientY + 10) + 'px'; + tooltip.style.display = 'block'; + + isPinned = true; + } + } + }); + + closeButton.addEventListener('click', function() { + tooltip.style.display = 'none'; + isPinned = false; + }); + } + + // set event handlers function paramHandler(event) { setParam(event.target.name, event.target.value);