Python ipyleaflet 将地图导出为 PNG 或 JPG 或 SVG

问题描述 投票:0回答:4

我尝试使用 ipyleaflet 将数据可视化导出为 PNG 或任何其他文件格式,但我找不到有效的方法。例如,在 folium 中有 map.save(path)。 ipyleaflet 中是否有我在研究中错过的库或方法可以帮助我实现我的目标?

这里是一些生成地图的示例代码

from ipyleaflet import *
center = [34.6252978589571, -77.34580993652344]
zoom = 10
m = Map(default_tiles=TileLayer(opacity=1.0), center=center, zoom=zoom)
m

我想将此地图导出为图像文件,而不需要手动截图。

我找到了两个允许导出 javascript 传单地图的来源: https://github.com/aratcliffe/Leaflet.printhttps://github.com/mapbox/leaflet-image

不幸的是我无法在Python中使用它们。

python image export leaflet
4个回答
10
投票

我和我的同事找到了一个不错的 ipyleaflet (python) 图像导出解决方案。这是它的工作原理。导出需要 folium 库。本例中的 GeoJson 数据已经准备好样式属性:

import folium
map = folium.Map([51., 12.], zoom_start=6,control_scale=True)
folium.GeoJson(data).add_to(map)
map.save('map.html')

结果如下:output

可以通过子进程调用在 python(Windows)中进一步处理 html 文件,以将其生成 PDF 或 PNG。我希望这会有所帮助,因为 python 的 ipyleaflet 文档几乎不存在。


6
投票

要生成 html,您可以使用 ipywidgets

from ipywidgets.embed import embed_minimal_html
embed_minimal_html('map.html', views=[m])

如果你想制作PNG,你可以使用ipywebrtc,更具体地说:

或者用代码:

from ipywebrtc import WidgetStream, ImageRecorder
widget_stream = WidgetStream(widget=m, max_fps=1)
image_recorder = ImageRecorder(stream=widget_stream)
display(image_recorder)

保存PNG:

with open('map.png', 'wb') as f:
    f.write(image_recorder.image.value)

或者转换为枕头图像进行预处理:

import PIL.Image
import io
im = PIL.Image.open(io.BytesIO(image_recorder.image.value))

0
投票

ipyleaflet 支持另存为 html。好像不支持导出svg和png。

https://ipyleaflet.readthedocs.io/en/latest/map_and_basemaps/map.html#save-to-html

m.save('output.html')

我为 ipyleaflet 创建了问题单:

https://github.com/jupyter-widgets/ipyleaflet/issues/1083


0
投票

我使用这个函数将地图转换为PIL图像(然后将其保存为PNG、JPEG等)

import ipyleaflet
from PIL import Image
import requests
import math

# Save a map view in a PIL image
def toImage(m):

    # Bounds and zoom of the current view
    (latmin,lonmin),(latmax,lonmax) = m.bounds
    zoom = m.zoom

    # URLs of all the Tilelayer on the map
    baseUrls = [x.url for x in m.layers if type(x) == ipyleaflet.leaflet.TileLayer]

    # Opacities
    opacities = [x.opacity for x in m.layers if type(x) == ipyleaflet.leaflet.TileLayer]

    # Convert lat/lon/zoom to xtile,ytile.
    # See https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
    def latlon2tile(lat_deg, lon_deg, zoom):
        lat_rad = (lat_deg * math.pi) / 180.0
        n = math.pow(2,zoom)
        xtile = n * ((lon_deg + 180.0) / 360.0)
        ytile = n * (1 - (math.log(math.tan(lat_rad) + 1.0/math.cos(lat_rad)) / math.pi)) / 2
        return xtile, ytile

    xtile1f,ytile2f = latlon2tile(latmin,lonmin, zoom)
    xtile2f,ytile1f = latlon2tile(latmax,lonmax, zoom)

    xtile1 = int(xtile1f)
    xtile2 = int(xtile2f)
    ytile1 = int(ytile1f)
    ytile2 = int(ytile2f)

    # Amount of pixels to crop on each side
    dx1 = 256*(xtile1f-xtile1)
    dx2 = 256*(xtile2+1-xtile2f)
    dy1 = 256*(ytile1f-ytile1)
    dy2 = 256*(ytile2+1-ytile2f)

    dx1 = round(dx1*100)//100
    dx2 = round(dx2*100)//100
    dy1 = round(dy1*100)//100
    dy2 = round(dy2*100)//100

    # Number of tiles
    nx = xtile2 - xtile1 + 1
    ny = ytile2 - ytile1 + 1

    # Dimension of the overall image
    w = 256 * nx
    h = 256 * ny
    imageTotal = Image.new(mode="RGBA", size=(w,h))

    # Substitute x,y,z into a TileService URL
    def url(baseurl, x,y,zoom):
        return baseurl.replace('{x}',str(int(x))).replace('{y}',str(int(y))).replace('{z}',str(int(zoom)))

    # Cycle on all tiles and compose the overall image
    for x in range(nx):
        xt = xtile1 + x
        xpos = x*256
        for y in range(ny):
            yt = ytile1 + y
            ypos = y*256
            for baseurl,opacity in zip(baseUrls,opacities):
                try:
                    image = Image.open(requests.get(url(baseurl,xt,yt,zoom), stream=True).raw)
                    image = image.convert('RGBA')

                    if opacity < 1.0:
                        # Split image in 4 channels
                        (r,g,b,a) = image.split()

                        # Change the alpha channel
                        # See https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.eval
                        a = Image.eval(a, lambda px: opacity*px)

                        # Merge 4 channels
                        image = Image.merge('RGBA',(r,g,b,a))

                    # Transparent paste!!!
                    # See https://stackoverflow.com/questions/5324647/how-to-merge-a-transparent-png-image-with-another-image-using-pil
                    imageTotal.paste(image, (xpos,ypos), mask=image)
                except:
                    pass

    # Crop the image
    area_crop = (dx1, dy1, w-dx2, h-dy2)
    return imageTotal.crop(area_crop)

m = ipyleaflet.Map(max_zoom=21, center=[47, 12], zoom=5, scroll_wheel_zoom=True, basemap=ipyleaflet.basemaps.OpenTopoMap)
display(m)

然后在笔记本的另一个单元格中:

toImage(m)

该函数请求覆盖地图所有 TileLayer 的当前地图边界所需的图块并构建整体图像,同时管理图层的不透明度。

© www.soinside.com 2019 - 2024. All rights reserved.