将带有geojson图层的Leaflet贴图导出到Angular 5中的png

时间:2018-04-29 23:16:30

标签: javascript angular leaflet html2canvas

我有一张带有大量geojson的传单地图。我希望能够将地图图像导出为png。我已经尝试了导出基本映射的leaflet-image插件,我也尝试(独立地)html2canvas库将map div的内容输出到png文件。

不幸的是,使用leaflet-image输出只显示背景图层(地图)而不显示任何geojson区域,而使用html2canvas输出结果显示的是图层而不是背景图。

我不认为传单图像插件是可行的,因为它有太多的约束。它需要将所有图层渲染到画布,但这样做,性能潜水 - 它似乎无法处理如此多的地理区域。我也没有太多控制服务器,并且没有安装nodejs。

html2canvas更有希望。使用该文件和文件保护程序,我能够输出完整的图像和区域,如下所示:

html2canvas(myMap, {
            allowTaint:false,
            useCORS: true,
            width: 650,
            height: 500,

        }).then(canvas => {
            canvas.toBlob((blob) => {
                saveAs(blob, "my-map.png");
            });
        });

除了一个问题外,这个工作非常出色 - 区域在画布上呈现的位置错误。也就是说,区域偏移是错误的。这是一个例子。地图的底部和右侧应覆盖带有彩色等值区域的边缘: leaflet image with regions, clipped

如何在地图上正确渲染区域,而不是在错误的位置剪切?

编辑:我看了一下html2canvas网站here上的问题看起来很有希望。我似乎没有这样的转换问题,但是有人经历了我认为的层偏移问题。我在这里实施了他们的解决方案:

function redraw() {
    var lat_tmp = map.getCenter().lat;
    var lng_tmp = map.getCenter().lng;
    map.setView([-66.22149259832975, -1.142578125]);
    setTimeout(function () {
        waitForTilesToLoad()
    }, 50000);
    map.setView([lat_tmp, lng_tmp]);
}

但它也没有用,所以我仍然试图解决这个问题。这不是我遇到的层偏移问题,而是层剪裁问题。

6 个答案:

答案 0 :(得分:1)

最近有同样的问题,在尝试管理html2canvas时也使用了相同的引用主题。

就我而言(也许你也是这样),地图是动态的,带有太多的瓷砖和各种元素(markeclusters,piecharts等)。使用CraigVA给出的技巧搞砸了。

最后,我尝试了其他几个插件,发现这个插件非常令人满意:

Leaflet Easy Print

没有故障,干净的png导出。

答案 1 :(得分:0)

我已经完成了一些工作,将geojson转换为使用背景图块与上面的html2canvas链接结合视口。如果我移动了地图,我确实让它正确映射,但是当我缩放时却没有。这是非常棘手的,如果我可以避免它,我宁愿不去做。这可能是更糟糕的做法,就像传单改变一样,解决方案会破裂。

dom-to-image为我提供了从dom生成图像所需的一切。 Leaflet Easy Print不仅仅为我提供了什么样的图像。控制台中存在dom-to-image错误,使用简易打印时会出现相同的错误。我也想自己控制图像导出。 dom-to-image的缺点是它只适用于chrome和firefox。没有IE,没有Edge,没有Safari。所以我把这个作为一个可能的解决方案发布给那些不需要担心这些约束的人,​​同时希望以后能够提出更好的解决方案。

dom-to-image是一个很好的发现,因为我也可以将它与其他对象一起使用,例如图表。感谢Potemkin帮助我走上这条道路。

这是使用domtoimage和文件保护程序的解决方案:

grid.PerformCallback("Completed:" +id, function(data){
    // do whatever
});

Util.encodeByteArray方法是我自己的方法,用于将图像字符串转换为字节数组,以创建可用于导出的blob。如果图像显然变得很大,那就有问题了,尽管我从未发生过这种情况。可能有更好的方法,我目前还没有意识到这一点。

import { saveAs } from "file-saver";
import domtoimage from "dom-to-image";

domtoimage.toPng(myMapDomObject).then((myImage: string) => {
            const commaPos = myImage.indexOf(",");
            if (commaPos > 0) myImage = myImage.substring(commaPos + 1);
            const byteArray = Util.encodeByteArray(myImage);
            const blob = new Blob([byteArray], { type: "image/png" });
            saveAs(blob, "mymap.png");
        });

顺便说一句,我正在研究leaflet.browser.print的工作原理。它似乎能够产生相同的结果并且是跨浏览器,但仅提供打印。如果我能弄清楚如何导出内部图像,那么在GitHub上调查它的代码可能会提供更多的跨浏览器解决方案。

答案 2 :(得分:0)

提示:

  

地图的底部和右侧应覆盖彩色彩色区域:

我找到了解决这个问题的简单方法!

  1. 设置渲染器填充值(https://leafletjs.com/reference-1.4.0.html#renderer-padding)= 0
  2. 设置“ windowWidth”值(http://html2canvas.hertzen.com/configuration)= document.documentElement.clientWidth。

希望对您有所帮助!祝你好运。

答案 3 :(得分:0)

尝试从地图中删除渲染器填充,然后导出。它对我有用。

在您的情况下,类似的东西

myMap.getRenderer(myMap).options.padding = 0;
html2canvas(...

答案 4 :(得分:0)

自编写此问题以来,已经过去了相当长的时间。最终对我有用的解决方案是购买Telerik Reporting并实施我在那里需要的任何映射解决方案。这是一个非常好的选择,并且一直为我工作,因此,如果您有机会这样做,我强烈建议您这样做。您还可以立即使用多种不同的文件格式,该解决方案看起来更加专业。只是要考虑的一个选择。

答案 5 :(得分:0)

我使用dom-to-image库结束工作,因为我将图块图像剪切到边界框,而传单图像未使用css clip属性。

  // Need an arrow function to preserve this context since this is being used as a callback.
  private _maskTileLayer = (layer: TileLayer, bounds: LatLngBounds): void => {
    const northWest = bounds.getNorthWest(); // top-left px latLng
    const southEast = bounds.getSouthEast(); // bottom-right px latlng
    const northWestPoint = this._map.latLngToLayerPoint(northWest);
    const southEastPoint = this._map.latLngToLayerPoint(southEast);
    const container = layer.getContainer();
    container.style.position = 'absolute';
    container.style.clip = `rect(${northWestPoint.y}px, ${southEastPoint.x}px, ${southEastPoint.y}px, ${northWestPoint.x}px)`;
  };

我得到的是带有黑匣子的较大地图图像。我认为这是地图窗格的缓冲区。甚至使用

map.getRenderer(map as any).options.padding = 0;

对我不起作用。

我发现我必须设置图像的宽度和高度选项以匹配地图容器客户端的宽度/高度。

  public mapAsJpegDataUrl(map: Map): Promise<string> {
    const mapPane = map.getPane('mapPane');
    const container = map.getContainer();
    const width = container.clientWidth;
    const height = container.clientHeight;
    const dataUrl = domToImage.toJpeg(mapPane, { width, height });
    return dataUrl;
  }

该库明显比传单图像慢,但它的作用也更大。