tdt-map-sdk

免费开源的天地图 JS API 4.0 轻量封装:核心层(纯地图能力)+ 组件层(开箱即用的搜索面板/工具栏),PC / H5 通用,React / Vue 可用,样式已内联无需单独引入 CSS,高德/百度地图的免费替代方案
674
Install
npm install tdt-map-sdk
Latest Version:1.3.2
License:MIT
Last Updated:Oct 9, 2026
Links: npm
Maintainer: mingle998

tdt-map-sdk

免费开源的天地图 JS API 4.0 轻量封装 —— 核心层(纯地图能力)+ 组件层(开箱即用的搜索面板/工具栏),PC / H5 通用,样式已内联无需单独引 CSS。

💯 完全免费:MIT 协议开源,无任何收费点、无功能阉割,商用无需向本 SDK 付费。地图底图由国家地理信息公共服务平台(天地图)提供,注册即可获取免费 key。

⚠️ 天地图官方对免费 key 设有调用额度,个人与企业的授权范围不同,商用上线前请以天地图官方条款为准。

sdk-img


✨ 特性

  • 💯 免费开源:MIT 协议,无收费点、无需向本 SDK 付费,个人与商用项目均可自由使用
  • 🎯 开箱即用:一行代码获得完整地图组件(搜索面板 + 工具栏 + H5 半屏)
  • 📦 双层设计:核心层 12.5KB / 组件层 31.5KB(gzip 后更小),按需选择
  • 🌐 框架无关:React / Vue / Angular / 原生 HTML 通用
  • 📱 多端适配:PC 浮动卡片、H5 可拖拽半屏自动切换
  • 🔧 零依赖:运行时不依赖任何第三方库
  • 💪 TypeScript:完整类型声明
  • 🇨🇳 国内合规:底图来自天地图,坐标符合国测标准,无境外服务依赖,国内访问速度快

🚀 快速开始

第一步:获取免费的天地图 key

本 SDK 免费,但需要一枚天地图开发者密钥(key)才能加载底图。key 免费申请,注册即可获得:

  1. 打开 天地图开发者平台,注册并登录
  2. 进入「控制台 → 应用管理 → 创建应用」,应用类型选 浏览器端
  3. 在应用里点击「添加 key」,授权域名 填你的站点域名(本地开发可填 localhost)
  4. 复制生成的 key 填入下面的代码

本地调试如果不想填域名限制,可先留空或用 localhost;上线前记得补上正式域名,否则浏览器控制台会报 403。 天地图的免费 key 有调用额度限制,个人与企业的授权范围不同,商用项目请以官方条款为准。

安装

npm install tdt-map-sdk

组件层:开箱即用(推荐)

一行代码,自带搜索面板、工具栏、H5 半屏:

import TdtMapWidget from 'tdt-map-sdk/widget';

new TdtMapWidget({
    el: 'app',
    key: '你的天地图key'
});

无需引入 CSS(样式已内联进 JS)

React 示例
import { useEffect, useRef } from 'react';
import TdtMapWidget from 'tdt-map-sdk/widget';

export default function MapPage() {
    const containerRef = useRef(null);
    const widgetRef = useRef(null);

    useEffect(() => {
        widgetRef.current = new TdtMapWidget({
            el: containerRef.current,
            key: '你的天地图key'
        });
        return () => widgetRef.current?.destroy();
    }, []);

    return <div ref={containerRef} style={{ width: '100vw', height: '100vh' }} />;
}
Vue 示例
<template>
    <div ref="mapContainer" style="width: 100%; height: 100vh;"></div>
</template>

<script setup>
import { onMounted, onBeforeUnmount, ref } from 'vue';
import TdtMapWidget from 'tdt-map-sdk/widget';

const mapContainer = ref(null);
const widget = ref(null);

onMounted(() => {
    widget.value = new TdtMapWidget({
        el: mapContainer.value,
        key: '你的天地图key'
    });
});

onBeforeUnmount(() => widget.value?.destroy());
</script>
CDN 直接引入
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <script src="https://unpkg.com/tdt-map-sdk@latest/dist/tdt-widget.min.js"></script>
</head>
<body>
    <div id="app"></div>
    <script>
        new TdtMapWidget({ el: 'app', key: '你的key' });
    </script>
</body>
</html>

核心层:纯地图能力

只需要地图 API,不需要面板 UI:

import TdtMap from 'tdt-map-sdk';

const map = new TdtMap({
    el: 'mapDiv',
    key: '你的天地图key',
    center: [116.40769, 39.89945],
    zoom: 12
});

map.search('北京大学', { count: 10 }, (err, res) => {
    if (!err) map.plotResults(res.pois);
});

📚 双层架构

tdt-map-sdk
│
├─ 核心层(TdtMap)          12.5 KB(含样式)
│   ├─ 地图加载、定位、缩放
│   ├─ 搜索 API、结果打点
│   ├─ 标记点、信息窗
│   └─ 全屏/小窗模式
│
└─ 组件层(TdtMapWidget)    31.5 KB(含样式)
    ├─ 包含核心层全部能力
    ├─ 搜索面板(地名搜索 + 坐标拾取)
    ├─ 右下工具栏(图层切换 + 定位按钮)
    └─ H5 可拖拽半屏(三档吸附、惯性滑动)

选择指南

需求 推荐方案 引入路径
快速集成,不想写 UI 组件层 tdt-map-sdk/widget
React / Vue 项目 组件层 tdt-map-sdk/widget
需要深度定制搜索面板 核心层 tdt-map-sdk
只需要地图能力 核心层 tdt-map-sdk
想要免费替代高德 / 百度 / 腾讯地图 本 SDK tdt-map-sdk/widget

为什么选天地图而不是高德 / 百度 / 腾讯?

对比项 本 SDK(天地图) 高德 / 百度 / 腾讯 JS API
SDK 授权费用 免费,MIT 协议 部分厂商 SDK 附带使用条款
底图服务费用 有免费额度,商用请查官方条款 基础量免费,超量/商用需付费
是否需实名认证 需注册天地图账号 多数需实名认证
坐标系 国测标准(CGCS2000) 各家自有偏移坐标(GCJ-02 / BD-09)
数据来源 国家地理信息公共服务平台 商业地图厂商
打包体积 核心 12.5KB / 组件 31.5KB 通常数百 KB
引入方式 npm / CDN,样式内联 npm / CDN,通常需额外引 CSS

如果你的项目追求无 SDK 授权费、体积极小、坐标系合规,本 SDK 是值得评估的免费替代方案。


🎯 组件层:TdtMapWidget

自带功能

PC 端
  • ✅ 左上浮动搜索面板(地名搜索 + 坐标拾取,双页签)
  • ✅ 右下工具栏(图层切换:矢量/影像/混合 + 定位按钮)
  • ✅ 搜索结果列表(可点击跳转)
H5 端
  • ✅ 底部可拖拽半屏(三档:收起 / 半屏 / 全屏)
  • ✅ 惯性滑动 + 吸附动画
  • ✅ 轻点把手切换档位

半屏档位由面板高度(clamp(420px, 55vh, 84vh))与固定露出高度计算,与屏幕高矮解耦,三档顺序不会因屏幕不同而翻转。搜索结果渲染导致面板高度变化时会自动重算档位。

API

class TdtMapWidget {
    constructor(options: {
        el: string | HTMLElement;      // 容器(必填)
        key: string;                   // 天地图 key(必填)
        center?: [number, number];     // 初始中心 [lng, lat]
        zoom?: number;                 // 初始缩放级别
        search?: boolean;              // 显示搜索页签(默认 true)
        coord?: boolean;               // 显示坐标页签(默认 true)
        tools?: boolean;               // 显示工具栏(默认 true)
        miniable?: boolean;            // 是否允许小窗模式(默认 false,显示小窗内的"展开"按钮)
        mode?: 'full' | 'mini';        // 初始模式(默认 'full')
        onReady?: (widget) => void;
        onError?: (error) => void;
        mapOptions?: {};               // 额外透传给 TdtMap 的选项
    });

    // 获取底层 TdtMap 实例
    getSDK(): TdtMap;

    // 获取组件根元素(挂载自定义节点 / 打样式补丁)
    getElement(): HTMLElement;

    // 地名搜索(结果自动渲染到面板)
    search(keyword: string, opts?: {
        count?: number;      // 默认 10
        plot?: boolean;      // 是否打点,默认 true
    }): this;

    // 周边搜索(默认以当前地图中心为圆心)
    searchNearby(keyword: string, radius?: number, opts?: {
        count?: number;      // 默认 10
        lng?: number;        // 指定圆心经度(不传则取地图中心)
        lat?: number;        // 指定圆心纬度
        plot?: boolean;      // 是否打点,默认 true
    }): this;

    // 定位到指定坐标
    locate(lng: number, lat: number, opts?: {}): boolean;

    // 切换图层:'vec' | 'sat' | 'hyb'
    switchLayer(type: string): this;

    // 切换页签:'search' | 'coord'
    switchTab(tab: string): this;

    // 切换模式:'full' | 'mini'
    setMode(mode: 'full' | 'mini'): this;

    // 读取当前模式
    getMode(): 'full' | 'mini';

    // H5:把底部半屏展开到全屏档(PC 端无半屏时无操作)
    openFull(): this;

    // 监听事件:'ready' | 'error' | 'search' | 'expand' | 'collapse'
    on(event: string, handler: Function): () => void;

    // 显示提示条
    toast(msg: string, duration?: number): this;

    // 销毁组件
    destroy(): void;
}

小窗模式(mini)

组件支持在「铺满容器的全屏模式」和「340×240 小窗模式」之间切换,用于地图作为辅助工具而非主界面的场景(后台管理页角落挂一张图、客服工作台侧边预览等)。

const widget = new TdtMapWidget({
    el: 'app',
    key: '你的天地图key',
    mode: 'mini',      // 初始就渲染为小窗
    miniable: true     // 显示小窗右上角的"展开"按钮
});

// 代码控制切换
widget.setMode('full');      // 展开为全屏
widget.setMode('mini');      // 收回小窗
widget.getMode();            // 读取当前模式:'full' | 'mini'

// 监听切换(可用于同步宿主页面自身的布局)
widget.on('expand',   () => console.log('已展开到全屏'));
widget.on('collapse', () => console.log('已收起到小窗'));

行为说明:

  • setMode('mini') 时,卡片缩为居中 340×240 小窗(可通过 CSS 覆盖),搜索面板与右下工具栏自动隐藏,miniable: true 时显示展开按钮,点击即回到全屏。
  • 模式切换后组件会自动调用底层 checkResize(),并在 320ms 后补一次,避免地图在小窗中拉伸变形。
  • mode: 'mini' 与 miniable 相互独立:只设 mode: 'mini' 而不设 miniable 时不会出现展开按钮,适合由宿主自己的按钮来控制模式。
  • 小窗尺寸改由 CSS 决定,宿主可覆盖:
.tdt-widget.tdt-mode-mini .tdt-widget-card {
    width: 400px;
    height: 300px;
}

完整示例见 examples/mini-mode.html。

事件

const off = widget.on('search', (res) => {
    console.log('搜索到', res.pois.length, '条结果');
});

off();  // 调用返回值即可取消该监听
事件 触发时机 回调参数
ready 地图初始化完成 TdtMap 实例
error 初始化失败(key 错误、脚本加载失败等) Error
search 每次搜索返回(含无结果 / 行政区定位) { pois, area, resultType }
expand 由 mini 切回 full 无
collapse 由 full 切到 mini 无

调用底层 API

const widget = new TdtMapWidget({ el: 'app', key: 'xxx' });
const map = widget.getSDK();  // 获取底层 TdtMap 实例

// 调用核心层的任何方法
map.on('click', (e) => console.log(e.lnglat));
map.addMarker(116.4, 39.9, { title: '自定义点' });

样式定制

/* 修改主题色 */
:root {
    --tdt-accent: #ff6b6b;
    --tdt-accent-light: #ffe0e0;
}

/* 修改搜索面板宽度 */
.tdt-widget-panel { width: 400px; }
恢复原生滚动条

SDK 默认隐藏所有 .tdt-* 元素的滚动条(含搜索结果列表),如需恢复:

.tdt-widget-results {
    scrollbar-width: auto;
    -ms-overflow-style: auto;
}
.tdt-widget-results::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    display: block;
}

🔧 核心层:TdtMap

API

class TdtMap {
    constructor(options: {
        el: string | HTMLElement;
        key: string;
        center?: [number, number];
        zoom?: number;
        mapType?: 'vec' | 'sat' | 'hyb';
        onReady?: (instance) => void;
        onError?: (error) => void;
    });

    // 定位到指定坐标
    locate(lng: number, lat: number, opts?: {
        zoom?: number;
        title?: string;
        content?: string;
    }): boolean;

    // 添加标记点
    addMarker(lng: number, lat: number, opts?: {
        title?: string;
        content?: string;
        icon?: string;
    }): Marker | null;

    // 地名搜索
    search(keyword: string, opts: {
        count?: number;                // 返回条数,默认 10
        start?: number;                // 起始序号(分页)
        level?: number;                // 结果层级
        mapBound?: string;             // 限定搜索范围
    }, callback: (err, result) => void): void;

    // 周边搜索
    searchNearby(keyword: string, lng: number, lat: number, 
        radius: number, opts: {}, callback: Function): void;

    // 搜索结果打点
    plotResults(pois: Array): void;

    // 清除自定义标记点
    clearMarkers(): this;

    // 清除搜索结果标记点
    clearResultMarkers(): this;

    // 切换底图
    switchMap(type: 'vec' | 'sat' | 'hyb'): this;

    // 地理定位
    geolocate(opts?: {
        onError?: (error) => void;
    }): void;

    // 获取当前中心点
    getCenter(): [number, number] | null;

    // 获取当前缩放级别
    getZoom(): number | null;

    // 容器尺寸变化后手动触发地图重绘(宿主布局变动时调用)
    resize(): void;

    // 监听事件
    on(event: 'ready' | 'load' | 'click', handler: Function): void;

    // 切换模式
    setMode(mode: 'full' | 'mini'): this;

    // 读取当前模式
    getMode(): 'full' | 'mini';

    // 销毁地图
    destroy(): void;
}

// 静态成员
TdtMap.version;                    // 版本号
TdtMap.isValidMapType('sat');      // 校验底图类型:'vec' | 'sat' | 'hyb'

📦 包导出

{
  "exports": {
    ".": "./dist/tdt-map.esm.js",           // 核心层
    "./widget": "./dist/tdt-widget.esm.js", // 组件层
    "./core": "./dist/tdt-map.esm.js"       // 核心层别名
  }
}

📄 许可证

MIT


🙋 FAQ

Q: 真的完全免费吗?会不会有隐藏收费?

本 SDK 免费。以 MIT 协议开源,源码可自由使用、修改、商用,没有任何收费点或功能锁,也不需要向本 SDK 支付任何费用。

唯一的外部依赖是天地图 key,激活也是免费的,但天地图官方对免费 key 设有调用额度,且个人与企业账号的授权范围不同。个人学习、开源项目、中小流量站点通常够用;商用或无授权顾虑的场景,建议发布前查阅天地图官方最新条款,必要时申请更高配额。这部分属于天地图平台的政策,SDK 侧不做任何限制。

Q: 和直接调用天地图官方 JS API 有什么区别?

官方 API 只提供地图能力,搜索框、坐标拾取、图层按钮、H5 半屏这些 UI 都需要自己写。本 SDK 把这些封装好了,组件层一行代码即可获得完整交互界面;如果只想用纯地图能力,用核心层即可,体积仅 12.5KB。此外还补齐了官方 API 中较繁琐的搜索请求签名、结果解析与打点逻辑。

Q: 支持哪些框架?能在小程序里用吗?

支持所有能运行 JS 的环境:React、Vue、Angular、Svelte、原生 HTML 均可直接使用。微信小程序不支持,因为小程序没有 DOM 且禁止动态加载外部脚本,请改用天地图的小程序 SDK。

Q: 组件层是否包含核心层?

是的。TdtMapWidget 内部封装了 TdtMap,无需同时引入两者。

Q: 坐标怎么和高德 / 百度互转?

天地图使用国测标准坐标(CGCS2000,与 WGS84 在民用精度下可视为一致),高德/腾讯使用 GCJ-02,百度使用 BD-09。跨平台数据混用前需做坐标转换,否则会有几十到几百米的偏移。本 SDK 不做隐式转换,避免引入误差。

Q: 可以隐藏组件的某些部分吗?

可以。通过初始化选项控制:

new TdtMapWidget({
    el: 'app',
    key: 'xxx',
    search: false,  // 隐藏搜索页签
    coord: false,   // 隐藏坐标页签
    tools: false    // 隐藏右下工具栏
});

Q: 小窗模式(mini)怎么用?

初始化传 mode: 'mini' 即可渲染为小窗;miniable: true 会额外显示小窗内的展开按钮,点它回到全屏。也可随时用 widget.setMode('mini' | 'full') 切换,并用 widget.on('expand' | 'collapse', fn) 同步宿主页面布局。详见小窗模式(mini)。

Q: CSS 会污染全局吗?

不会。所有样式类都带 .tdt- 或 .tdt-widget- 前缀。


🔗 相关链接


v1.3.0 · Made with ❤️

Related Packages

@bonncloud/tianditu

天地图(Tianditu / 国家地理信息公共服务平台)全功能 TypeScript SDK —— JS API 4.0...

535
@movk/maplibre

声明式 MapLibre GL 封装库:MaplibreMap/Source/Layer 等组件与 composables,原生支...

1,028 0
iflow-engine-base

BIM Engine SDK for Vue2, Vue3, React and HTML

29,473

Version History

Version Released License
1.3.2 MIT
1.3.1 MIT
1.3.0 MIT
Pre-releases (1)
Version Released License
0.0.0-stage