Skip to content

MapLibre 集成

本页目的: 讲解如何在前端(MapLibre GL JS)渲染已制作好的 .pmtiles 底图(如 上海地区底图项目 裁剪出的 shanghai.pmtiles)。 只涉及渲染代码——数据就是那一个 .pmtiles 文件,无需下载任何额外数据

与「上海地区底图项目」第 5 步(test-map.html 快速验证)不同,本页系统讲解集成到自己应用的方法: 协议原理、@protomaps/basemaps 完整样式、React 组件、样式切换与性能。

🚀 本文方案的实战封装见原型 protomaps-map-view:把本页的 React 集成方式封装成通用 MapView 组件(中心/缩放、marker/轨迹、底图切换、可嵌入纯 HTML 或发 S3 分发)。 ⚠️ 3D 地形(TerrainControl)不在本页范围——它需要单独的 DEM 高程数据,与 Protomaps 底图无关。

1. 原理:pmtiles 自定义协议

MapLibre 默认不认识 .pmtiles 文件。通过 pmtiles npm 包的 Protocol 注册一个 自定义 URL 协议(pmtiles://),MapLibre 请求瓦片时就会走该协议处理器, 由它通过 HTTP Range Requests 从 PMTiles 文件中提取所需瓦片:

pnpm add pmtiles
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { Protocol } from "pmtiles";

// 注册 pmtiles 协议(全局一次即可)
const protocol = new Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);

关键点: source 的 url 必须以 pmtiles:// 为前缀,MapLibre 才会使用注册的协议处理器。

2. 最简集成

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      protomaps: {
        type: "vector",
        url: "pmtiles:///shanghai.pmtiles", // 本地相对路径
        attribution: '© <a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
      },
    },
    layers: [ /* 矢量图层定义 */ ],
  },
  center: [121.47, 31.23],
  zoom: 11,
});

URL 写法:

场景 url
本地相对路径(http-server) pmtiles:///shanghai.pmtiles
远程对象存储 pmtiles://https://cdn.example.com/shanghai.pmtiles

3. 完整底图样式:@protomaps/basemaps(推荐)

手写所有道路、水系、建筑、地名图层太繁琐。Protomaps 提供 npm 包自动生成完整样式:

pnpm add @protomaps/basemaps
import { layers, namedFlavor } from "@protomaps/basemaps";
import { Protocol } from "pmtiles";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";

const protocol = new Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    glyphs: "https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf",
    sprite: "https://protomaps.github.io/basemaps-assets/sprites/v4/light",
    sources: {
      protomaps: {
        type: "vector",
        url: "pmtiles:///shanghai.pmtiles",
        attribution: '© <a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
      },
    },
    layers: layers("protomaps", namedFlavor("light"), { lang: "zh" }),
  },
  center: [121.47, 31.23],
  zoom: 11,
});

layers() 函数签名

layers(
  sourceName: string,      // 对应 sources 中的 key
  flavor: Flavor,          // 风格:light / dark / white / black / grayscale
  options?: {
    lang?: string;         // 语言,如 "zh"、"en"、"ja"、"ko",默认英文
    labelsOnly?: boolean;  // 是否只显示文字标签
  }
)

支持的 Flavor(风格)

效果
namedFlavor("light") 浅色(默认)
namedFlavor("dark") 深色
namedFlavor("white") 纯白极简
namedFlavor("black") 纯黑
namedFlavor("grayscale") 灰度

中文标签: 设置 lang: "zh" 后,城市名称、道路名称等会使用中文渲染(如果 OSM 数据中有中文名)。

4. React 集成模板

import * as React from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { Protocol } from "pmtiles";
import { layers, namedFlavor } from "@protomaps/basemaps";

export const MapViewProtomaps = () => {
  const mapRef = React.useRef<HTMLDivElement>(null);

  React.useEffect(() => {
    const protocol = new Protocol();
    maplibregl.addProtocol("pmtiles", protocol.tile);

    const map = new maplibregl.Map({
      container: mapRef.current!,
      style: {
        version: 8,
        glyphs: "https://protomaps.github.io/basemaps-assets/fonts/{fontstack}/{range}.pbf",
        sprite: "https://protomaps.github.io/basemaps-assets/sprites/v4/light",
        sources: {
          protomaps: {
            type: "vector",
            url: "pmtiles:///shanghai.pmtiles",
            attribution: '© <a href="https://protomaps.com">Protomaps</a>',
          },
        },
        layers: layers("protomaps", namedFlavor("light"), { lang: "zh" }),
      },
      center: [121.47, 31.23],
      zoom: 11,
    });

    map.addControl(new maplibregl.NavigationControl(), "top-right");

    return () => { map.remove(); }; // 防内存泄漏
  }, []);

  return <div ref={mapRef} style={{ width: "100%", height: "500px" }} />;
};

React 要点: Map 必须在 useEffect 内创建,cleanup 中 map.remove()

5. 底图切换(可选)

const switchToSatellite = () => {
  const mapStyle = map.getStyle();
  mapStyle.sources = {
    satellite: {
      type: "raster",
      tiles: ["https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"],
      tileSize: 256,
    },
  };
  mapStyle.layers = [{ id: "satellite", type: "raster", source: "satellite" }];
  map.setStyle(mapStyle);
};

setStyle() 会重置地图状态(控件、图层),需要重新添加。

6. 性能清单

  • map.remove() 在组件卸载时调用
  • 事件监听在 useEffect cleanup 中移除
  • pmtiles:// 协议全局只注册一次
  • 大量点数据用 Layer + GeoJSON source(而非 Marker)
  • 生产环境将 PMTiles 放到 CDN / R2(免流量费)

7. 参考链接

资源 链接
MapLibre GL JS 文档 https://maplibre.org/maplibre-gl-js/docs/
MapLibre PMTiles 集成指南 https://docs.protomaps.com/pmtiles/maplibre
@protomaps/basemaps API https://docs.protomaps.com/basemaps/maplibre
MapLibre Demo Tiles https://demotiles.maplibre.org/
底图下载与预览 https://maps.protomaps.com/
实战封装(原型) protomaps-map-view — 把本页集成方式封装成通用 MapView 组件(React + Vite,可嵌入纯 HTML / 发 S3 分发)

→ 上一站:上海地区底图项目

Backlinks (2)
flowchart LR
  n0["AI"]
  n1["Database"]
  n2["Dev Tools"]
  n3["Emoji"]
  n4["Frontend"]
  n5["Game Dev"]
  n6["Collection"]
  n7["Languages"]
  n8["Maps"]
  n9["Media"]
  n10["Monitor"]
  n11["Plans"]
  n12["Cloud"]
  n13["对象存储基本用法(Bucket / Object / 常用操作)"]
  n14["对象存储数据迁移(R2 → MinIO / 跨厂商搬迁)"]
  n15["Object Storage"]
  n16["挂载 Bucket 为本地文件系统(FUSE Mount)"]
  n17["对象存储签名 URL(Signed URL)原理与实战"]
  n18["对象存储供应商对比(S3 / R2 / OSS / Supabase / MinIO)"]
  n19["Infrastructure"]
  n20["Prototypes"]
  n21["Research"]
  n22["Better Auth 源码阅读指南"]
  n23["DuckDB 环境与基本使用"]
  n24["DuckDB 实战研究"]
  n25["DuckDB 模拟数据"]
  n26["PostgreSQL 数据用 DuckDB 加速查询"]
  n27["HK 角色阅读"]
  n28["HK 台词阅读"]
  n29["Hollow Knight 英语主题"]
  n30["HK 物品阅读"]
  n31["HK 地点阅读"]
  n32["HK 世界观 / Lore 阅读"]
  n33["HK 英语学习素材清单"]
  n34["英语学习 Dashboard"]
  n35["English Scraps Archive"]
  n36["English Scraps 使用指南"]
  n37["Jellyfin 源码阅读指南"]
  n38["Lux 资料整理"]
  n39["Nest Commander 学习资料"]
  n40["NestJS 源码阅读指南"]
  n41["Protomaps 自建底图研究"]
  n42["自制 PMTiles 地图(最简单例子)"]
  n43["MapLibre 集成 Protomaps"]
  n44["PMTiles 格式与工具链"]
  n45["上海地区底图项目"]
  n46["Redash 源码阅读指南"]
  n47["Rust 学习计划"]
  n48["shadcn/ui 源码阅读指南"]
  n49["TRIP 项目核心原理与代码阅读指南"]
  n1 --> n24
  n6 --> n0
  n6 --> n1
  n6 --> n2
  n6 --> n3
  n6 --> n4
  n6 --> n5
  n6 --> n7
  n6 --> n8
  n6 --> n9
  n6 --> n10
  n6 --> n11
  n6 --> n21
  n12 --> n15
  n13 --> n14
  n13 --> n17
  n13 --> n18
  n13 --> n20
  n14 --> n13
  n14 --> n16
  n14 --> n17
  n14 --> n18
  n15 --> n13
  n15 --> n14
  n15 --> n16
  n15 --> n17
  n15 --> n18
  n16 --> n13
  n16 --> n17
  n16 --> n18
  n17 --> n20
  n18 --> n13
  n18 --> n14
  n18 --> n17
  n18 --> n20
  n19 --> n12
  n20 --> n13
  n20 --> n16
  n20 --> n17
  n20 --> n18
  n20 --> n41
  n21 --> n22
  n21 --> n24
  n21 --> n34
  n21 --> n37
  n21 --> n38
  n21 --> n39
  n21 --> n40
  n21 --> n41
  n21 --> n46
  n21 --> n47
  n21 --> n48
  n21 --> n49
  n23 --> n25
  n24 --> n1
  n24 --> n21
  n24 --> n23
  n24 --> n25
  n24 --> n26
  n25 --> n23
  n25 --> n26
  n26 --> n24
  n26 --> n25
  n29 --> n27
  n29 --> n28
  n29 --> n30
  n29 --> n31
  n29 --> n32
  n29 --> n33
  n29 --> n36
  n33 --> n28
  n34 --> n29
  n34 --> n35
  n34 --> n36
  n36 --> n34
  n36 --> n35
  n41 --> n8
  n41 --> n42
  n41 --> n43
  n41 --> n44
  n41 --> n45
  n42 --> n44
  n43 --> n20
  n43 --> n45
  n44 --> n42
  n44 --> n45
  n45 --> n43
  n45 --> n44
  click n0 "../../../../collection/ai/" "AI"
  click n1 "../../../../collection/database/" "Database"
  click n2 "../../../../collection/dev-tools/" "Dev Tools"
  click n3 "../../../../collection/emoji/" "Emoji"
  click n4 "../../../../collection/frontend/" "Frontend"
  click n5 "../../../../collection/game-dev/" "Game Dev"
  click n6 "../../../../collection/" "Collection"
  click n7 "../../../../collection/languages/" "Languages"
  click n8 "../../../../collection/maps/" "Maps"
  click n9 "../../../../collection/media/" "Media"
  click n10 "../../../../collection/monitor/" "Monitor"
  click n11 "../../../../collection/scraps/plans/" "Plans"
  click n12 "../../../../knowledge/infrastructure/cloud/" "Cloud"
  click n13 "../../../../knowledge/infrastructure/cloud/object-storage/basic-usage/" "对象存储基本用法(Bucket / Object / 常用操作)"
  click n14 "../../../../knowledge/infrastructure/cloud/object-storage/data-migration/" "对象存储数据迁移(R2 → MinIO / 跨厂商搬迁)"
  click n15 "../../../../knowledge/infrastructure/cloud/object-storage/" "Object Storage"
  click n16 "../../../../knowledge/infrastructure/cloud/object-storage/mount-bucket/" "挂载 Bucket 为本地文件系统(FUSE Mount)"
  click n17 "../../../../knowledge/infrastructure/cloud/object-storage/signed-url/" "对象存储签名 URL(Signed URL)原理与实战"
  click n18 "../../../../knowledge/infrastructure/cloud/object-storage/vendors-comparison/" "对象存储供应商对比(S3 / R2 / OSS / Supabase / MinIO)"
  click n19 "../../../../knowledge/infrastructure/" "Infrastructure"
  click n20 "../../../../prototypes/" "Prototypes"
  click n21 "../../../" "Research"
  click n22 "../../better-auth/" "Better Auth 源码阅读指南"
  click n23 "../../duckdb/basic-usage/" "DuckDB 环境与基本使用"
  click n24 "../../duckdb/" "DuckDB 实战研究"
  click n25 "../../duckdb/mock-data/" "DuckDB 模拟数据"
  click n26 "../../duckdb/postgresql-acceleration/" "PostgreSQL 数据用 DuckDB 加速查询"
  click n27 "../../english/hollow-knight/characters/" "HK 角色阅读"
  click n28 "../../english/hollow-knight/dialogues/" "HK 台词阅读"
  click n29 "../../english/hollow-knight/" "Hollow Knight 英语主题"
  click n30 "../../english/hollow-knight/items/" "HK 物品阅读"
  click n31 "../../english/hollow-knight/locations/" "HK 地点阅读"
  click n32 "../../english/hollow-knight/lore/" "HK 世界观 / Lore 阅读"
  click n33 "../../english/hollow-knight/resources/" "HK 英语学习素材清单"
  click n34 "../../english/" "英语学习 Dashboard"
  click n35 "../../english/scraps/archive/" "English Scraps Archive"
  click n36 "../../english/scraps/" "English Scraps 使用指南"
  click n37 "../../jellyfin/" "Jellyfin 源码阅读指南"
  click n38 "../../lux/" "Lux 资料整理"
  click n39 "../../nest-commander/" "Nest Commander 学习资料"
  click n40 "../../nestjs/" "NestJS 源码阅读指南"
  click n41 "../" "Protomaps 自建底图研究"
  click n42 "../make-own-map/" "自制 PMTiles 地图(最简单例子)"
  click n43 "./" "MapLibre 集成 Protomaps"
  click n44 "../pmtiles/" "PMTiles 格式与工具链"
  click n45 "../shanghai-map/" "上海地区底图项目"
  click n46 "../../redash/" "Redash 源码阅读指南"
  click n47 "../../rust/" "Rust 学习计划"
  click n48 "../../shadcn-ui/" "shadcn/ui 源码阅读指南"
  click n49 "../../trip/" "TRIP 项目核心原理与代码阅读指南"
Links (2)