使用 L.esri.DynamicMapLayer,是否可以在动态地图上绑定鼠标悬停事件而不是弹出窗口? Here is a working example

问题描述

我知道将弹出窗口绑定到 ESRI 的 L.esri.DynamicMapLayer here。下面的代码是成功的。

$.ajax({
      type: 'GET',url: url + '?f=json',data: { layer: fooType },dataType: 'json',success: function(json) {

          var foo_layer = fooLayers[fooType].layers;
          
          foo = L.esri.dynamicMapLayer({
            url: url,layers: [foo_layer],transparent: true
          }).addTo(map).bringToFront();

          foo.bindPopup(function(error,featureCollection) {

            if (error || featureCollection.features.length === 0) {
              return false;
            } else {
              var obj = featureCollection.features[0].properties;
              var val = obj['Pixel Value'];
              var lat = featureCollection.features[0].geometry.coordinates[1];
              var lon = featureCollection.features[0].geometry.coordinates[0];
            
              new L.responsivePopup({
                autopanPadding: [10,10],closeButton: true,autopan: false
              }).setContent(parseFloat(val).toFixed(2)).setLatLng([lat,lon]).openOn(map);

            }
          });
      }        
});

但不是 click 响应,我想知道您是否可以在动态地图上使用 mouSEOver 代替 bindTooltip。我查看了 L.esri.DynamicMapLayerdocumentation,它说它是 L.ImageOverlay 的扩展。但是,here 中可能存在我不完全理解的问题。也许它甚至不相关。

此外,我一直在测试即使是最简单的代码的多种变体,以使其在下面工作,但没有成功。也许因为这是异步行为,所以不可能。寻找任何指导和/或解释。非常新手程序员,非常需要专业知识。

$.ajax({
      type: 'GET',transparent: true
          }).addTo(map).bringToFront();

          foo.bindTooltip(function(error,featureCollection) {

            if (error || featureCollection.features.length === 0) {
              return false;
            } else {
              new L.tooltip({ 
                sticky: true
              }).setContent('blah').setLatLng([lat,lng]).openOn(map);

            }
          });
      }        
});

解决方法

巧合的是,我一直在研究 different problem,该问题的一个副产品可能对您有用。

您的主要问题是点击事件的异步性质。如果你打开你的地图(你评论中的第一个 jsfiddle),打开你的开发工具网络选项卡,然后开始点击,你会看到每次点击都会产生一个新的网络请求。这就是许多 esri 查询函数的工作方式 - 它们需要查询服务器并检查数据库中给定的 latlng 中您想要的值。如果您尝试将相同的行为附加到 mousemove 事件,您将触发大量网络请求,并使浏览器过载 - 坏消息。

您可以做的一个解决方案是读取从 esri 图像服务返回的图像光标下的像素数据,它还有很多工作要做。如果您知道光标下像素的确切 rgb 值,并且您知道该 rgb 值在地图图例中对应的值,则可以实现您的结果。

Here is a working example

Here 是代码和框源代码。不要害怕刷新,CSB 转换模块的方式有点奇怪。

这里发生了什么?让我们一步一步看:

  1. loadzoomendmoveend 之类的地图事件上,一个专门的函数正在获取与 L.esri.dynamicMapLayer 相同的图像,使用名为 EsriImageRequest 的东西,这是我写的一个类,重用了很多 esri-leaflet 的内部逻辑:
map.on("load moveend zoomend resize",applyImage);

const flashFloodImageRequest = new EsriImageRequest({
  url: layer_url,f: "image",sublayer: "3",});


function applyImage() {
  flashFloodImageRequest
    .fetchImage([map.getBounds()],map.getZoom())
    .then((image) => {
      //do something with the image
    });
}

EsriImageRequest 的一个实例具有 fetchImage 方法,该方法采用 L.LatLngBounds 数组和地图缩放级别,并返回一个图像 - 与您的 dynamicMapLayer 显示在地图。

EsriImageRequest 可能是您不需要的额外代码,但我刚好遇到了这个问题。我写这个是因为我的应用程序在 nodejs 服务器上运行,而且我没有带有 L.esri.dynamicMapLayer 的地图实例。作为一种更简单的替代方法,您可以定位显示您的 dynamicMapLayer 的传单 DOM <img> 元素,将其用作我们在第 2 步中需要的图像源。您必须在 {{ 1}} 属性,并在该侦听器中运行 src。如果您不熟悉 Leaflet 如何管理 DOM,请查看检查器中的元素选项卡,您可以在此处找到 applyImage 元素:

set up a listener

我建议这样做,不是我的示例所示的方式。就像我说的,我碰巧刚刚在研究一个相关的问题。

  1. 在代码的前面,我设置了一个画布,并使用 css <img>positionpointer-events 属性,它正好位于地图上,但设置为不进行交互(我在示例中给了它少量的不透明度,但您可能希望将不透明度设置为 0)。在 opacity 函数中,我们将得到的图像写入画布:
applyImage

现在我们有一个不可见的画布,它的像素内容与 dynamicMapLayer 的完全相同。

  1. 现在我们可以监听地图的 // earlier... const mapContainer = document.getElementById("leafletMapid"); const canvas = document.getElementById("mycanvas"); const height = mapContainer.getBoundingClientRect().height; const width = mapContainer.getBoundingClientRect().width; canvas.height = height; canvas.width = width; const ctx = canvas.getContext("2d"); // inside applyImage .then: .then((image) => { image.crossOrigin = "*"; ctx.drawImage(image,width,height); }); 事件,并从我们创建的画布中获取鼠标的 rgba 像素值。如果您阅读我的 enter image description here,您会看到我如何获得图例值数组,以及我如何使用该数组将像素的 rgba 值映射回该颜色的图例值。我们可以使用该像素的图例值,并将弹出内容设置为该值。
mousemove

如您所见,我还将弹出窗口的 map.on("mousemove",(e) => { // get xy position on cavnas of the latlng const { x,y } = map.latLngToContainerPoint(e.latlng); // get the pixeldata for that xy position const pixelData = ctx.getImageData(x,y,1,1); const [R,G,B,A] = pixelData.data; const rgbvalue = { R,A }; // get the value of that pixel according to the layer's legend const value = legend.find((symbol) => compareObjectWithTolerance(symbol.rgbvalue,rgbvalue,5) ); // open the popup if its not already open if (!popup.isOpen()) { popup.setLatLng(e.latlng); popup.openOn(map); } // set the position of the popup to the mouse cursor popup.setLatLng(e.latlng); // set the value of the popup content to the value you got from the legend popup.setContent(`Value: ${value?.label || "unknown"}`); }); 设置为鼠标所在的位置。在弹出选项中使用 latlng,它的行为很像工具提示。我尝试使用适当的 closeButton: false 让它工作,但我自己遇到了一些麻烦。这似乎产生了相同的效果。

对不起,如果这是一个很长的答案。有很多方法可以调整/改进我的代码示例,但这应该可以帮助您入门。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...