在 React 中使用 leaflet.offline ?

Tmf*_*ang 7 javascript leaflet reactjs react-leaflet

我正在开发一个 React 应用程序,并且正在尝试实现一个支持离线下载地图图块的 Leaflet 地图。为此,我想到使用 leaflet.offline (https://github.com/allartk/leaflet.offline),但我不确定如何在 React 中使用这个库?

如何将 leaflet.offline 与 React 结合使用,或者是否有任何特定于 React 的库支持使用 Leaflet 离线下载地图图块?

谢谢你的帮助!

Tmf*_*ang 9

编辑:好的,所以旧的解决方案工作得很好,但它需要直接从Leaflet Offline包下载文件,并修改它们,这并不理想。我现在已经成功地将 Leaflet Offline 包与 npm 和 React 一起使用。我建议首先阅读下面发布的旧解决方案,因为新解决方案是在此基础上构建的。这就是我所做的:

更新的新解决方案:

1 - 离线安装 Leaflet: npm install leaflet.offline@next

2 - 使用以下命令将 Leaflet Offline 包导入到您的 React 组件中:

import L from "leaflet"; // Remember that this must also be imported
import "leaflet.offline";
Run Code Online (Sandbox Code Playgroud)

3 - 现在看看旧解决方案中的步骤 6。我们现在分别使用 L.tileLayer.offline 和 L.control.savetiles,而不是使用“generateOfflineTilelayer”和“generateControlSavetiles”。如果您像我一样使用 TypeScript,请在​​代码行上方添加 ts-ignore 作为注释,以防止由 TypeScript 引起的任何错误。因此,第 6 步中的 useEffect 代码现在如下所示:

useEffect(() => {
  if(map){

    // @ts-ignore
    const tileLayerOffline = L.tileLayer.offline(
      "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
      {
        attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
        minZoom: 13,
      }
    );

    tileLayerOffline.addTo(map);

    // @ts-ignore
    const controlSaveTiles = L.control.savetiles(
      tileLayerOffline, 
      {
        zoomlevels: [13, 14, 15, 16], // optional zoomlevels to save, default current zoomlevel
      }
    );

    controlSaveTiles.addTo(map!);
  }
}, [map]);
Run Code Online (Sandbox Code Playgroud)

4 - 地图现在应该按预期工作。在尝试导入 leaflet.offline 时,我确实遇到了缺少声明文件的问题。对我来说,这个问题奇怪地只存在于我的一个组件中,而不存在于其他组件中。如果您遇到同样的问题,您可以尝试按照 IDE 的建议创建一个声明文件,或者您可以尝试在 import 语句的正上方添加“// @ts-ignore”。希望这对某人有帮助:)


旧解决方案:

好吧,所以我从来没有在网上找到任何人可以解决这个问题,但经过一些尝试和错误后,我已经解决了这个问题。我之所以希望能够在 Leaflet 中下载地图供离线使用,是因为我正在使用 Ionic、Capacitor 和 React 制作一个手机应用程序。可能值得一提的是我也在使用打字稿。

为了其他人的利益,我将在下面发布有关如何实现此工作的指南。希望这能防止其他人经历我所经历的同样的挫折:D

所以我在使用 Leaflet.offline 插件时遇到的问题是 Leaflet.tileLayer.offline 或 Leaflet.control.saveTiles 无法被识别(可能是由于使用了 React)。以下是我为使离线地图正常工作而采取的步骤:

1 - 我最初是通过 npm 包使用 leaflet.offline 。我们想要更改插件,以便我们不再依赖插件对 Leaflet.tileLayer.offline 或 Leaflet.control.saveTiles 的使用。我们想要的是创建一些方法来返回这两个提到的类的实例,然后我们可以将这些方法导入到我们的 React 组件中。最简单的方法是不通过 npm 使用插件,而是直接下载插件的文件。因此,您应该做的第一件事是下载在插件的 GitHub 页面( https://github.com/allartk/leaflet.offline/tree/master)上找到的 leaflet.offline/src 文件夹内容的副本/src)。您应该下载的文件是 ControlSaveTiles.js、TileLayerOffline.js 和 TileManager.js。

2 - 现在在您的 React 项目中创建一个文件夹。我将我的名称命名为 LeafletMapOfflinePlugin,但您可以将其命名为任何您想要的名称。然后将您下载的文件移动到该文件夹​​中。

3 - 打开 TileLayerOffline.js 文件,并将以下代码粘贴到文件底部。这将导出一个可用于创建 TileLayerOffline 类的新实例的函数,以便我们不再依赖 L.tileLayer.offline:

// Export a function that generates an offfline tilelayer-object, 
// as the above expansion of L.tileLayer.offline doesn't work
export function generateOfflineTilelayer(url, options) {
  return new TileLayerOffline(url, options)
}
Run Code Online (Sandbox Code Playgroud)

4 - 打开 ControlSaveTiles.js 文件,并将以下代码粘贴到文件底部。这将导出一个可用于创建 ControlSaveTiles 类的新实例的函数,以便我们不再依赖 L.control.saveTiles:

// Export a function that generates a savetiles-object,
// as the above expansion of L.control.savetiles doesn't work
export function generateControlSavetiles(baseLayer, options) {
  return new ControlSaveTiles(baseLayer, options);
}
Run Code Online (Sandbox Code Playgroud)

5 - 现在可以使用以下导入将它们导入到任何 React 组件中:

import { generateOfflineTilelayer } from "<path-to-TileLayerOffline.js>";
import { generateControlSavetiles } from "<path-to-ControlSaveTiles.js>";
Run Code Online (Sandbox Code Playgroud)

6 - 下面是如何在 React 组件(使用 TypeScript)中使用它的简单示例。这里我们使用useState存储MapContainer对象,并在useEffect中使用它来手动设置tileLayer和控件:

import L, { Map } from "leaflet";
import { MapContainer } from "react-leaflet";
import { generateOfflineTilelayer } from "<path-to-TileLayerOffline.js>";
import { generateControlSavetiles } from "<path-to-ControlSaveTiles.js>";
import React, { useState } from "react";


const LeafletMap: React.FC<LeafletMapProps> = () => {
  const [map, setMap] = useState<Map | undefined>();
  
  useEffect(() => {
    if(map){
      const tileLayerOffline = generateOfflineTilelayer(
        "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
        {
          attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
          minZoom: 13,
        }
      );

      tileLayerOffline.addTo(map);

      const controlSaveTiles = generateControlSavetiles(
        tileLayerOffline, 
        {
          zoomlevels: [13, 14, 15, 16], // optional zoomlevels to save, default current zoomlevel
        }
      );

      controlSaveTiles.addTo(map!);
    }
  }, [map]);
  
  return(
    <MapContainer
      style={{ width: "100vw", height: "20vh" }}
      center={[63.446827, 10.421906]}
      zoom={13}
      scrollWheelZoom={false}
      whenCreated={setMap}
    >
    </MapContainer>
  )
}
Run Code Online (Sandbox Code Playgroud)

7 - 您现在应该有一个带有控制元素的简单 Leaflet-map,当“+”(不要与用于缩放的“+”混淆。“+”是 leaflet.offline 的控制元素的默认值时,并且可以通过查看 leaflet.offline 的文档进行更改)单击后,它会下载并保存地图上当前可见区域的地图图块。如果需要,您可以转到网络浏览器的网络选项卡来检查这些图块的下载。单击“-”(再次强调,不要与缩放按钮混淆)时,所有下载的图块都会被删除。请参见下图,了解使用 leaflet.offline 的 ControlSaveTiles 时地图的外观;用于保存图块的控制元素位于用于缩放的控制元素下方:

传单离线地图示例

好了,差不多就这样结束了。希望这对某人有帮助:D 还有与传单地图相关的提示;如果您在仅显示地图的一小部分时遇到问题,请尝试使用map.invalidateSize()(如果您的地图容器存储为“地图”)。这将重置并更新传单地图已知的设备宽度,这有望使地图正确渲染:D