在 React 中使用 leaflet.offline?

Using leaflet.offline with React?

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

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

感谢您的帮助!

编辑:好的,所以旧的解决方案工作得很好,但是它需要直接从 Leaflet Offline 包中下载文件,然后修改它们,这是不理想的。我现在已经设法将 Leaflet Offline 包与 npm 和 React 一起使用。我建议首先阅读下面的旧解决方案 posted,因为新解决方案是建立在它之上的。这是我所做的:

更新的新解决方案:

1 - 使用以下命令离线安装 Leaflet: npm install leaflet.offline@next

2 - 将 Leaflet 离线包导入到您的 React 组件中:

import L from "leaflet"; // Remember that this must also be imported
import "leaflet.offline";

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

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


旧解决方案:

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

为了其他人的利益,我将 post 下面的指南介绍我是如何做到这一点的。希望这会阻止其他人经历与我相同的挫折 :D

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

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

2 - 现在在您的 React 项目中创建一个文件夹。我将我的 LeafletMapOfflinePlugin 命名为 LeafletMapOfflinePlugin,但您可以随意命名。然后把你下载的文件移动到这个文件夹。

3 - 打开 TileLayerOffline.js 文件,将以下代码粘贴到文件底部。这将导出一个函数,可用于创建 TileLayerOffline class 的新实例,这样我们就不再依赖 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)
}

4 - 打开 ControlSaveTiles.js 文件,将以下代码粘贴到文件底部。这将导出一个函数,可用于创建 ControlSaveTiles class 的新实例,这样我们就不再依赖 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);
}

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

import { generateOfflineTilelayer } from "<path-to-TileLayerOffline.js>";
import { generateControlSavetiles } from "<path-to-ControlSaveTiles.js>";

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

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

好了,到此为止。希望这对某人有所帮助 :D 也是与 Leaflet 地图相关的提示;如果您在只显示一小部分地图时遇到问题,请尝试使用 map.invalidateSize() (如果您的地图容器存储为“地图”)。这将重置并更新传单地图已知的设备宽度,这有望使地图正确呈现:D