传单地图截图

Take a screenshot of leaflet map

所以我有一张传单地图,我在上面画了一些图层(矩形)。 我想捕获该特定图层的屏幕截图,而不是整个可见地图。 我已经尝试过传单插件,但它们并没有像我期望的那样工作,而且我已经设法捕获了屏幕截图,但是使用 html2canvas 捕获了地图的整个可见部分。 我怎么能 select (捕获)只有我想在屏幕截图中的那个矩形?

或者在使用 leaflet-area-select 时可能捕获 selected 区域?

我正在使用 React 和 TypeScript。

这可以借助 leaflet-simple-map-screenshoter 和一些仔细的图像处理来完成。

Working codesandbox

这是我所做的演练:

自定义窗格

首先,让我们创建一些 custom panes 来区分镜头中我们想要的和不需要的。实际上,您所需要的只是屏幕截图中您不需要的图层,但我们会创建两个以备不时之需:

//Create a map and assign it to the map div
var map = L.map("leafletMapid", mapOptions);

// Create some custom panes
map.createPane("snapshot-pane");
map.createPane("dont-include");

现在我们可以在这些窗格中创建我们的层,无论它们是图块层还是 geojson 或路径或其他什么。让我们在“快照窗格”的镜头中包含我们想要的 baselayer 和一个 geojson,在“不包含”窗格的镜头中包含我们不想要的路径:

// Add baselayer and geojson to snapshot pane
const baselayer = L.tileLayer(url,
  { pane: "snapshot-pane" }
).addTo(map);
const greekborder = L.geoJSON(greekBorderGeoJson, {
  pane: "snapshot-pane"
}).addTo(map);

// Add another polygon to the 'dont-include' pane
const serbianborder = L.polyline(serbianBorder, {
  color: "darkred",
  pane: "dont-include"
}).addTo(map);

设置屏幕截图:

使用截图插件,让我们设置一个截图程序。截屏器将排除 我们在“不包含”面板中设置的图层:

const snapshotOptions = {
  hideElementsWithSelectors: [
    ".leaflet-control-container",
    ".leaflet-dont-include-pane",
    "#snapshot-button"
  ],
  hidden: true
};

// Add screenshotter to map
const screenshotter = new SimpleMapScreenshoter(snapshotOptions);
screenshotter.addTo(map);

自定义截图功能

我们不想使用默认的屏幕截图行为,因此我们将指定一个函数,以便在我们点击自定义按钮时触发

// define screenshot function
const takeScreenShot = () => {
  // defined below
}

// Add takescreenshot function to button
const button = document.getElementById("snapshot-button");
button.addEventListener("click", takeScreenShot);

如果你查看截图插件的文档,你可以在截图后的 .then 中捕获截图者创建的图像数据:

const takeScreenShot = () => {
  
screenshotter
    .takeScreen("image")
    .then((image) => {
      // Create <img> element to render img data
      var img = new Image();
    })

}

我们已经获取了图像数据,创建了一个新的 Image 元素,我们准备将图像数据分配给 new Image 元素。但在我们这样做之前,我们要定义一个 img.onload 函数,它将在图像数据分配给 Image 元素时触发。这个onload必须先定义,然后我们才能分配图像源数据。

图像加载时会发生什么

加载图像时,我们想做一些事情

  1. 找出相关地图特征的边界
  2. 将这些边界转换为屏幕上的像素坐标
  3. 包含在canvas
  4. 范围内的图像数据写入
  5. 将 canvas 另存为 png 文件

这是执行此操作的代码:

img.onload = () => {
  // Create canvas to process image data
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  // Set canvas size to the size of your resultant image
  canvas.width = imageSize.x;
  canvas.height = imageSize.y;

  // Draw just the portion of the whole map image that contains
  // your feature to the canvas
  ctx.drawImage(
    img,
    topLeft.x,
    topLeft.y,
    imageSize.x,
    imageSize.y,
    0,
    0,
    imageSize.x,
    imageSize.y
  );

  // Create URL for resultant png
  var imageurl = canvas.toDataURL("image/png");

  const resultantImage = new Image();
  resultantImage.style = "border: 1px solid black";
  resultantImage.src = imageurl;

  // Append new image to body for nice visual in this example answer
  document.body.appendChild(canvas);

  canvas.toBlob(function (blob) {
    // saveAs function installed as part of leaflet snapshot package
    saveAs(blob, "greek_border.png");
  });
};

花点时间看一下,如果您有任何问题,请告诉我。

一个Image.onload函数在图像src赋值时触发,所以我们先定义函数,然后赋值src:

const takeScreenShot = () => {
  
screenshotter
    .takeScreen("image")
    .then((image) => {
      // Create <img> element to render img data
      var img = new Image();

      img.onload = () => {
        // all that code in the prev code block
      }

      // set the image source to what the snapshotter captured
      // img.onload will fire AFTER this
      img.src = image;
    })

}

就是这样。您将下载一个图像,其中包含地图的所有层,控件除外,以及您在“不包含”窗格中排除的任何内容。图像将被裁剪到您的特征的边界。

请注意,如果部分地图项位于可见地图边界之外,这将不起作用。虽然可以使用地图边界之外的部分功能实现您想要的效果,但要做到这一点 复杂得多 ,尤其是如果您希望在背景中使用 baselayer。

另外,你说你最初使用的是 React。如果您在反应项目中使用传单,我强烈建议您使用反应传单。在您指定不使用它之前,我最初将此答案写为 react-leaflet 答案,但这里是 react-leaflet 版本:

React-leaflet codesandbox