npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wtfs-mapbox

v0.1.12

Published

天地图WTFS integration for Mapbox GL: custom layer and control (ESM/UMD).

Readme

Mapbox 加载天地图“三维地名(WTFS)”服务集成指南

本指南针对使用 Mapbox GL JS 加载“天地图三维地名(WTFS)”服务,说明如何通过 wtfs-mapbox 插件(图层/控件)解析服务端瓦片并呈现中文地名标注。 参考资料:天地图“三维地名(WTFS)”帮助文档 插件提供 ESM 与 UMD 两种构建形式,支持服务端样式优先与用户样式优先两种策略。

前置准备

  • mapbox-glprotobufjs 必须在你的项目中安装(本包声明为 peer 依赖):
    • mapbox-gl@^3
    • protobufjs@^7
  • 天地图访问令牌 tk(向天地图官方申请)。
  • 子域信息(通常为 t0~t7)。

安装

npm i wtfs-mapbox mapbox-gl protobufjs

导入方式

ESM(推荐)

import WTFSLayer from 'wtfs-mapbox';
import WTFSControl from 'wtfs-mapbox/control';

UMD(浏览器脚本标签 / CDN)

<script src="https://unpkg.com/mapbox-gl@2/dist/mapbox-gl.js"></script>
<script src="https://unpkg.com/protobufjs@7/dist/protobuf.min.js"></script>
<script src="https://unpkg.com/wtfs-mapbox/dist/wtfs-plugin.umd.js"></script>
<script src="https://unpkg.com/wtfs-mapbox/dist/wtfs-control.umd.js"></script>
<script>
  // 全局变量名:WTFSLayer / WTFSControl
  const layer = new WTFSLayer({ /* ...options */ });
  const control = new WTFSControl({ /* ...options */ });
  // map.addLayer(layer) 或 map.addControl(control)
  // 具体 options 配置见下文

</script>

快速开始(React 示例:加载天地图 WTFS 地名)

以下示例包含“获取当前位置瓦片”和“将当前位置瓦片作为初始化瓦片加载”的两个按钮,方便在初始化后无需移动/缩放即可加载地名。已将 Mapbox Access Token 与天地图 tk 替换为提示占位文本。

import React, { useEffect, useRef, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import WTFSLayer from 'wtfs-mapbox';
import 'mapbox-gl/dist/mapbox-gl.css';

export default function AppLayer() {
  const mapRef = useRef(null);
  const containerRef = useRef(null);
  const layerRef = useRef(null);
  const [seedTiles, setSeedTiles] = useState([]);

   // 获取当前位置应加载的初始瓦片,并存入状态
  const handleGetCurrentTiles = () => {
    const layer = layerRef.current;
    if (!layer) return;
    const tiles =
      (typeof layer.getInitTiles === 'function' && layer.getInitTiles()) ||
      [];
    setSeedTiles(tiles);
    console.log('手动获取当前初始瓦片集合:', tiles);
  };

  // 将已获取的瓦片作为初始化瓦片进行加载渲染
  const handleInitWithCurrentTiles = () => {
    const layer = layerRef.current;
    if (!layer) return;
    const tiles = (seedTiles && seedTiles.length)
      ? seedTiles
      : ((typeof layer.getInitTiles === 'function' && layer.getInitTiles())|| []);
    if (!tiles || !tiles.length) return;
    if (typeof layer.initTDT === 'function') {
      layer.initTDT(tiles);
    }else{
      console.error('initTDT 方法不存在,检查插件版本');
    }
  };

  useEffect(() => {
    if (!containerRef.current) return;

    // 设置 Mapbox Access Token(请替换为你自己的 Token)
    mapboxgl.accessToken = '<你的 Mapbox Access Token>';

    const map = new mapboxgl.Map({
      container: containerRef.current,
      projection: 'globe',
      style: 'mapbox://styles/mapbox/standard',
      center: [117.273, 33.668],
      zoom: 5.86,
      pitch: 60,
      bearing: -12,
      hash: true,
      locale: {
        'NavigationControl.ZoomIn': '放大',
        'NavigationControl.ZoomOut': '缩小',
        'NavigationControl.Compass': '旋转',
      },
    });

    map.addControl(new mapboxgl.NavigationControl({ visualizePitch: true }));

    const token = '<你的天地图 tk>';
    const tdtUrl = 'https://t{s}.tianditu.gov.cn/';
    const subdomains = ['t0', 't1', 't2', 't3'];

    map.on('style.load', async () => {
      const layer = new WTFSLayer({
        subdomains,
        metadata: {
          boundBox: { minX: -180, minY: -90, maxX: 180, maxY: 90 },
          minLevel: 1,
          maxLevel: 20,
        },
        aotuCollide: true,
        collisionPadding: [5, 10, 8, 5],
        serverFirstStyle: false,
        debugger: true,
        // 用户样式示例:覆盖颜色等,其余沿用默认布局
        layerStyle: {
          layout: {
            "text-size": [
              "coalesce",
              ["get", "fontSize", ["get", "label"]],
              ["get", "fontSize"],
              20,
            ],
            "text-font": [
              "literal",
              ["Noto Sans SC Regular", "Noto Sans Italic"],
            ],
            "symbol-placement": "point",
            "text-allow-overlap": false,
          },
          paint: {
            // "text-color": "#00ff00",
            //这是默认的颜色配置
            "text-color": [
              "coalesce",
              ["get", "fillColor", ["get", "label"]],
              "#ffffff",
            ],
            // 数据源一般文字白色 海洋描边 #0080ff 非海洋描边 #000000
            "text-halo-color": [
              "coalesce",
              ["get", "outlineColor", ["get", "label"]],
              "#ffffff",
            ],
            "text-halo-width": [
              "coalesce",
              ["get", "outlineWidth", ["get", "label"]],
              0,
            ],
          },
        },
        getTileUrl(z, x, y) {
          return (
            tdtUrl +
            'mapservice/GetTiles?lxys={z},{x},{y}&VERSION=1.0.0&tk=' +
            token
          );
        },
      });

      // 添加到地图并保存引用
      map.addLayer(layer);
      mapRef.current = map;
      layerRef.current = layer;

      // 可选:示例以包含江苏省范围瓦片初始化(替换为你的场景)
      layer.initTDT([
          {
              "x": 26,
              "y": 5,
              "level": 5, //实际请求瓦片 z = level+1
              "boundBox": {
                  "minX": 112.5,
                  "maxX": 123.75,
                  "maxY": 33.75,
                  "minY": 22.5
              }
          },
      ]);
    });

    map.on('error', (e) => {
      console.error('Mapbox 错误:', e && e.error ? e.error : e);
    });

    return () => {
      if (mapRef.current) {
        mapRef.current.remove();
        mapRef.current = null;
      }
    };
  }, []);

  return (
    <div style={{ width: '100%', height: '100%', position: 'relative' }}>
      <div style={{ width: '100%', height: '100%' }} ref={containerRef} />
      <button
        type="button"
        onClick={handleGetCurrentTiles}
        style={{ position: 'absolute', top: 60, left: 12, zIndex: 1, padding: '6px 10px' }}
      >
        获取当前位置瓦片
      </button>
      <button
        type="button"
        onClick={handleInitWithCurrentTiles}
        style={{ position: 'absolute', top: 100, left: 12, zIndex: 1, padding: '6px 10px' }}
      >
        将当前位置瓦片作为初始化瓦片加载
      </button>
    </div>
  );
}

注意:示例中的 URL 与 Token 为占位示例。不同环境/接口的路径、参数名可能不同(如是否为 WMTS、是否需要 tilematrix/tilerow/tilecol、是否区分中文/英文地名层等)。请以你的 WTFS 接口文档为准,并替换为有效的 Token。

效果预览

  • 初始化仅加载一个瓦片(示例)

    初始化仅一个瓦片

  • 点击“将当前位置瓦片作为初始化瓦片加载”后,按当前场景加载全部瓦片(示例)

    加载全部瓦片

样式策略与合并

  • serverFirstStyle: true(推荐):以服务端样式为主,使用插件内置的默认表达式读取服务端属性(例如 properties.label.*)。在此模式下,我们会用“默认样式覆盖用户样式”,确保服务端的样式语义优先。
  • serverFirstStyle: false:完全采用用户指定的 layerStyle;若未提供则回退到默认样式。

样式合并规则(浅合并):

  • serverFirstStyle: truelayout = {...user.layout, ...default.layout}paint = {...user.paint, ...default.paint}
  • serverFirstStyle: falselayout = {...default.layout, ...user.layout}paint = {...default.paint, ...user.paint}

选项说明(面向 WTFS)

  • sourceId / layerId:创建的数据源与图层 ID。默认:wtfs-labels / wtfs-label-layer
  • serverFirstStyle: boolean:是否以服务端样式为主(详见上文)。
  • layerStyle: { layout?: object; paint?: object; }:用户样式(在 serverFirstStyle: false 时优先)。
  • subdomains: string[]:天地图子域数组(如 ['t0',...,'t7'])。
  • getTileUrl(x, y, z, s):瓦片 URL 构造器,s 为选中的子域。
  • getIcoUrl(id, s):图标资源 URL 构造器。
  • getRoadTileUrl(x, y, z, s):道路/辅助瓦片 URL 构造器(若你的服务有路网地名层)。
  • debugger: boolean:输出样式化调试日志。

字体与字形

  • Mapbox 的 text-font 是样式中定义的字体栈名,不是系统字体名。若 WTFS/glyph 服务不包含 CJK 字形,可在 Mapbox 初始化时设置 localIdeographFontFamily 使用系统中文字体(如“微软雅黑”)。
  • 示例:localIdeographFontFamily: "'Microsoft YaHei','Arial Unicode MS'"
  • 如使用自定义 glyph 服务,请确保字体栈名称与 text-font 一致。

常见问题与排障

  • 地名不显示或乱码:检查 tk 是否有效、跨域/Referer 是否满足、URL 是否正确、是否存在 CJK glyph 或开启本地字体回退。
  • 图层未添加成功:查看控制台日志(启用 debugger),检查是否有 map.addLayer 报错或 ID 冲突。

导出与入口

  • 子路径导出:
    • wtfs-mapbox(插件主入口)
    • wtfs-mapbox/control(控件入口)
  • ESM 与 UMD:
    • exports["."].importdist/wtfs-plugin.esm.js
    • exports["."].requiredist/wtfs-plugin.umd.js
    • exports["./control"].importdist/wtfs-control.esm.js
    • exports["./control"].requiredist/wtfs-control.umd.js

许可与使用条款

  • 使用天地图服务需遵守其官方授权与使用条款,请确保你的 tk 与访问方式合规。