繁体   English   中英

在Angular 5中将带有geojson图层的传单地图导出为png

[英]Exporting Leaflet map with geojson layers to png in Angular 5

我有一张包含大量 geojson 的传单地图。 我希望能够将地图图像导出为 png。 我试过leaflet-image插件,它导出底图,我也试过(独立)html2canvas库将地图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");
            });
        });

这非常有效,除了一个问题 - 区域在画布上呈现在错误的位置上。 即区域偏移量是错误的。 这是一个例子。 地图的底部和右侧应该用彩色的等值线区域覆盖到边缘: 带有区域的传单图像,已剪裁

如何让区域在地图上正确渲染,而不是在错误的地方被剪裁?

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

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]);
}

但它也不起作用,所以我仍在努力解决这个问题。 我遇到的并不是真正的图层偏移问题,而是图层剪切问题。

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

在我的情况下(也许你也是),地图是动态的,承载了太多的图块和元素(markeclusters、饼图等)。 使用 CraigVA 给出的技巧把它搞砸了。

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

传单简易打印

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

我从上面的 html2canvas 链接中,在移动 geojson 以将视口与背景图块结合起来方面做了大量工作。 如果我移动地图,我确实让它正确映射,但当我放大时却没有。 这是非常棘手的事情,如果我可以避免的话,我宁愿不这样做。 这可能是更糟糕的方法,好像传单改变了,解决方案就会失效。

dom-to-image 为我提供了从 dom 生成图像所需的一切。 Leaflet Easy Print 比 dom-to-image 给了我什么。 控制台有dom-to-image的错误,使用leaflet easy print时出现同样的错误。 我也想自己控制图像导出。 dom-to-image 的缺点是它只适用于 chrome 和 firefox。 没有 IE,没有 Edge,没有 Safari。 因此,我已将此发布为不需要担心这些限制的人的可能解决方案,同时希望以后能提出更好的解决方案。

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

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

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");
        });

Util.encodeByteArray 方法是我自己的方法,用于将图像字符串转换为字节数组以创建可用于导出的 blob。 如果图像明显变大就会出现问题,尽管我从未发生过这种情况。 可能有更好的方法来做到这一点,我目前不知道。

export class Util {
    static encodeByteArray(bytes: string) {
        const byteCharacters = atob(bytes);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        return byteArray;
    }
}

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

语气 说:

地图的底部和右侧应该用彩色的等值线区域覆盖到边缘:

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

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

我希望这可以帮助你! 祝你好运。

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

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

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

自从写这个问题以来,已经过去了一段时间。 最终对我有用的解决方案是购买 Telerik Reporting 并在其中实施我需要的任何映射解决方案。 这是一个非常好的选择并且一直对我有用,所以如果你有机会这样做,我强烈推荐它。 您还可以立即使用多种不同的文件格式,并且该解决方案看起来更加专业。 只是一个可以考虑的选择。

我最终使用了 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;
  }

这个库明显比传单图像慢,但它也做得更多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM