更新记录

v1.5.5(2026-08-13)

天地图 UTS 原生插件(uni-app x 蒸汽模式)

基于 MapLibre(Android/iOS/Web)与华为 MapKit TileOverlay(鸿蒙)的天地图原生地图插件,GPU 渲染,四端统一 API。

插件市场 IDbeige-tdt-map


功能概览

模块 功能

平台兼容性

uni-app x(5.13)

Chrome Safari Android iOS 鸿蒙 微信小程序
75 12.1 10.0 12 20 -

其他

多语言 暗黑模式 宽屏模式
× ×

天地图 UTS 原生插件(uni-app x 蒸汽模式)

基于 MapLibre(Android/iOS/Web)与华为 MapKit TileOverlay(鸿蒙)的天地图原生地图插件,GPU 渲染,四端统一 API。

插件市场 IDbeige-tdt-map


功能概览

模块 功能
🗺️ 底图 矢量 / 影像 / 地形三图切换,8 子域名并行加载
📍 标记 单标记、批量标记、文字标注,支持点击事件 @markertap,信息窗口气泡
📐 覆盖物 折线 / 圆 / 矩形 / 多边形,各平台原生渲染
🎯 覆盖物管理 ID 追踪系统,支持单个移除、显隐控制
🧭 坐标转换 GCJ02 → WGS84(三端纯算法,厘米级精度)
🔍 视野控制 放大/缩小/自适应/像素平移/多点视野/布局刷新
✋ 手势 缩放/拖拽开关,旋转/倾斜手势控制,最大/最小缩放限制
🔄 旋转倾斜 bearing 旋转(0-360°)/ tilt 倾斜(0-60°),手势与程序双控
🖐 交互增强 地图长按事件 @mapLongClick、相机移动完成事件 @cameraChange
📍 定位 显示/隐藏用户位置蓝点,获取当前 GPS 坐标(三端原生定位)
🖼️ 自定义瓦片 叠加外部 XYZ 瓦片图层(如天气、交通、专题图)
🧮 信息计算 Haversine 测距、最佳视野计算、坐标系查询、容器尺寸
🌐 天地图 API 地理编码 / 逆地理编码 / 关键字搜索 / 范围搜索 / 周边搜索
📸 截图 地图快照导出 PNG(iOS drawHierarchy / Android snapshot)
📡 生命周期 onResume / onPause / destroyMap

快速开始

安装

  1. 在 uni-app x 项目根目录创建 uni_modules/beige-tdt-map/
  2. 将本插件所有文件放入该目录
  3. 配置 API Key(见下方)

配置 API Key

uni_modules/beige-tdt-map/config.uts 中填写你的天地图 Key:

export default {
  apiKey: '你的天地图服务端Key'
}

⚠️ Key 类型必须匹配:原生 App 必须使用「服务端」或「Android 平台」类型 Key,不能使用浏览器端 Key(浏览器端 Key 校验 Referer 头,原生 App 不发送 Referer 导致 403)。

申请地址:https://console.tianditu.gov.cn

基础用法

<template>
  <view class="page">
    <tdt-map
      ref="mapRef"
      class="map"
      :latitude="39.9042"
      :longitude="116.4075"
      :zoom="12"
      map-type="vec"
      @mapReady="onMapReady"
      @mapClick="onMapClick"
      @markertap="onMarkerTap"
    />
  </view>
</template>

<script setup lang="uts">
import { ref } from 'vue'
import type { MapClickDetail } from '@/uni_modules/beige-tdt-map'

const mapRef = ref(null as any | null)
const isReady = ref(false)

function onMapReady() {
  isReady.value = true
  // 地图就绪后可调用 API
  mapRef.value?.placeMarker(39.9142, 116.4074, '天安门')
}

function onMapClick(detail: MapClickDetail) {
  console.log('地图点击:', detail.lat, detail.lng)
}

function onMarkerTap(detail: MapClickDetail) {
  console.log('标记点击:', detail.lat, detail.lng)
}
</script>

<style>
.page { flex: 1; }
.map { flex: 1; width: 100%; }
</style>

组件属性

属性 类型 默认值 说明
apiKey String '' 天地图 Key(优先级高于 config.uts)
latitude Number 39.9042 初始纬度
longitude Number 116.4075 初始经度
zoom Number 10 初始缩放级别(1-18)
mapType String 'vec' 地图类型:vec 矢量 / img 影像 / ter 地形

组件事件

事件 参数 说明
@mapReady 地图初始化完成
@mapClick { lat, lng } 地图空白区域点击
@markertap { lat, lng } 标记点点击(v1.2.0 新增
@mapLongClick { lat, lng } 地图长按(v1.4.0 新增
@cameraChange { lat, lng } 相机视野变化完成(v1.4.0 新增
@mapError { code, message } 错误回调

API 参考

通过 ref 调用组件暴露的方法:

const mapRef = ref(null as any | null)
mapRef.value?.方法名(...)

地图控制

方法 参数 说明
moveToCenter(lat, lng) 坐标 移动地图中心
zoomTo(level) 缩放级别 设置缩放级别
getCurrentCenter() 获取当前中心坐标
getCurrentZoom() 获取当前缩放级别
changeMapType(type) 'vec'/'img'/'ter' 切换地图类型
fitToBounds(minLat, minLng, maxLat, maxLng, padding) 边界+边距 自适应视野
zoomInOne() 放大一级
zoomOutOne() 缩小一级
setMinZoomLevel(zoom) 数值 最小缩放限制
setMaxZoomLevel(zoom) 数值 最大缩放限制
panToLocation(lat, lng, zoom?) 坐标+缩放 平移到指定位置
centerAtZoom(lat, lng, zoom) 坐标+缩放 居中并缩放
setZoomEnabled(bool) 布尔 缩放手势开关
setDragEnabled(bool) 布尔 拖拽手势开关
setMapMaxBounds(minLat, minLng, maxLat, maxLng) 边界 设置视野边界
🆕 doCheckResize() 触发布局刷新
🆕 doPlanBy(x, y) 像素偏移 像素级平移
🆕 setViewportPoints(points) 坐标数组 多点自适应视野

标记点

方法 返回值 说明
placeMarker(lat, lng, title, snippet?) number (覆盖物ID) 添加单个标记(v1.3.0 新增 snippet 参数
batchMarkers([...]) Array<number> 批量添加标记
clearAllMarkers() 清除所有标记
🆕 drawLabel(lat, lng, text) number 添加文字标注

📍 定位(v1.3.0 新增)

方法 说明
setShowUserLocation(enabled) 显示/隐藏用户位置蓝点
getCurrentLocation(callback, onError?) 获取当前 GPS 坐标,异步回调

💬 信息窗口(v1.3.0 新增)

方法 说明
showMarkerInfoById(id) 显示指定标记的信息窗口
hideMarkerInfoById(id) 隐藏指定标记的信息窗口

🖼️ 自定义瓦片(v1.3.0 新增)

方法 说明
addTileLayer(id, tileUrl) 添加 XYZ 瓦片图层
removeTileLayer(id) 移除指定瓦片图层

🔄 旋转与倾斜(v1.4.0 新增)

方法 参数 说明
setMapBearing(bearing) 0-360° 设置地图旋转角度(0°=正北,顺时针)
setMapTilt(tilt) 0-60° 设置地图倾斜角度(0°=垂直俯瞰)
getMapBearing() 获取当前旋转角度
getMapTilt() 获取当前倾斜角度
// 向东旋转 90°
mapRef.value?.setMapBearing(90)

// 倾斜 45° 俯瞰
mapRef.value?.setMapTilt(45)

// 回正
mapRef.value?.setMapBearing(0)
mapRef.value?.setMapTilt(0)

✋ 手势进阶(v1.4.0 新增)

方法 参数 说明
setRotateEnabled(enabled) 布尔 双指旋转手势开关
setTiltEnabled(enabled) 布尔 双指倾斜手势开关(鸿蒙 4.1.0+ 支持)
// 禁止用户旋转地图
mapRef.value?.setRotateEnabled(false)

覆盖物

方法 返回值 说明
drawPolyline(points, color, width) number 绘制折线
drawCircle(lat, lng, radius, color, fillColor) number 绘制圆
drawRectangle(minLat, minLng, maxLat, maxLng, color, fillColor) number 绘制矩形
drawPolygon(points, color, fillColor) number 绘制多边形
clearAllOverlays() 清除所有覆盖物

🆕 覆盖物管理(v1.2.0)

方法 参数 说明
removeOverlayById(id) overlay ID 按 ID 移除单个覆盖物
showOverlayById(id) overlay ID 按 ID 显示覆盖物
hideOverlayById(id) overlay ID 按 ID 隐藏覆盖物
// 示例:添加标记并追踪 ID
const markerId = mapRef.value?.placeMarker(39.9142, 116.4074, '北京')

// 隐藏它
mapRef.value?.hideOverlayById(markerId)

// 重新显示
mapRef.value?.showOverlayById(markerId)

// 永久移除
mapRef.value?.removeOverlayById(markerId)

坐标转换

方法 参数 说明
convertGcj02ToWgs84(lat, lng) GCJ02 坐标 返回 WGS84 坐标

截图 & 信息

方法 返回值 说明
takeSnapshot() ArrayBuffer PNG 截图数据
showCenter() 显示到 infoText 当前中心坐标
showZoom() 显示到 infoText 当前缩放级别
showBounds() 显示到 infoText 当前视野范围
getCurrentBounds() MapBoundsDetail 获取视野范围

🆕 信息计算(v1.2.0)

方法 参数 返回值 说明
calculateDistance(start, end) 两点坐标 number(米) Haversine 测距
calculateViewport(points) 坐标数组 { center, zoom } 计算最佳视野
getMapCode() string 返回 "EPSG:4326"
getMapSize() { width, height } 容器像素尺寸

🆕 天地图 Web API(v1.2.0)

方法 参数 说明
reverseGeocode(latlng, callback) 坐标 + 回调 逆地理编码(坐标→地址)
forwardGeocode(address, callback) 地址 + 回调 地理编码(地址→坐标)
keywordSearch(params, callback) 搜索参数 + 回调 关键字搜索
boundsSearch(params, callback) 搜索参数 + 回调 视野内搜索
nearbySearch(params, callback) 搜索参数 + 回调 周边搜索
// 逆地理编码示例
mapRef.value?.reverseGeocode(
  { lat: 39.9088, lng: 116.3975 },
  (data: UTSJSONObject) => {
    console.log('地址:', data.getString('formatted_address'))
  }
)

// 关键字搜索示例
mapRef.value?.keywordSearch(
  { keyWord: '酒店', start: '0', count: '10' },
  (data: UTSJSONObject) => {
    console.log('搜索结果:', JSON.stringify(data))
  }
)

// 周边搜索示例
mapRef.value?.nearbySearch(
  { keyWord: '餐厅', pointLonlat: '116.3975,39.9088', queryRadius: '1000' },
  (data: UTSJSONObject) => {
    console.log('周边:', JSON.stringify(data))
  }
)

平台兼容性

平台 支持 引擎
Android MapLibre Android
iOS MapLibre iOS
HarmonyOS 华为 MapKit TileOverlay
H5 / Web MapLibre GL JS(CDN 动态加载)
小程序 不支持

💡 Web 端说明:Web 平台通过条件编译(#ifdef WEB)自动切换为 MapLibre GL JS 实现,<native-view> 替换为 <view> 容器,MapLibre GL JS 从 unpkg CDN 按需加载。全部 58 个 API 与 App 端保持一致。


类型定义

type MapClickDetail = { lat: number; lng: number }
type MapBoundsDetail = { minLat: number; minLng: number; maxLat: number; maxLng: number }
type MapErrorDetail = { code: number; message: string }
type MapSizeDetail = { width: number; height: number }
type MapViewportDetail = { center: MapClickDetail; zoom: number }
type MapClickCallback = (detail: MapClickDetail) => void
type MapReadyCallback = () => void
type MapErrorCallback = (detail: MapErrorDetail) => void
type MarkerClickCallback = (detail: MapClickDetail) => void
type MapLongClickCallback = (detail: MapClickDetail) => void
type CameraChangeCallback = (detail: MapClickDetail) => void
type GeocodeCallback = (data: UTSJSONObject) => void

type SearchOptions = {
  keyWord: string
  mapBound?: string
  level?: string
  queryType?: string
  queryRadius?: string
  pointLonlat?: string
  start?: string
  count?: string
  specifyAdminCode?: string
}

版本历史

v1.5.5 ⚡(2026-08)

⚡ 优化:Android 双指缩放掉帧(中端机)

  • 根因:MapLibre 默认 pixelRatio = 设备屏幕密度(1080p 中端机约 2.75~3.0),双指缩放时整个 GL 表面每帧重采样,中端 GPU 光栅化负载过重导致掉帧(context7 验证 MapLibre 官方源码 MapLibreMapOptions.java
  • 初始化时 pixelRatio 限制为 2.0(渲染像素量约减半,清晰度损失轻微;低密度屏保持原值不受影响)
  • setPrefetchZoomDelta(2) 预加载相邻缩放级别瓦片,缩放时新层级瓦片更快就绪

v1.5.4 🐛(2026-08)

🐛 修复:搜索/地点搜索解析失败(天地图老搜索接口已下线)

根因:天地图老接口 api.tianditu.gov.cn/search 已下线(404 返回非 JSON),JSON.parse 对非 JSON 返回 null,null as UTSJSONObject 抛 "null cannot be cast" 崩溃;且 v2 响应结构变化(status 为对象、pois 在顶层)。

  • 四端(Android/iOS/鸿蒙/Web)搜索统一迁移到官方「地名搜索2.0」/v2/search(queryType=12 行政区划搜索,specify 默认 156000000=中国)
  • 四端 HTTP 请求解析全部增加 JSON.parse 判空防御(非 JSON 响应回调结构化错误,不再崩溃)
  • 组件内置搜索适配 v2 响应格式(status 对象 infocode==1000 判定、顶层 pois)
  • demo 页搜索展示适配 v2(infocode + 结果总数)

🐛 修复:Android 定位按钮点击无反应

根因:Android 6+ 需运行时定位授权,原生 LocationManager 不会自动弹授权框(仅 uni.getLocation 等框架 API 会自动弹框),无权限时静默失败;且失败信息仅 console.error,用户看不到任何反馈。

  • Android 端 getUserLocation 新增运行时权限检查与申请UTSAndroid.requestSystemPermission 官方 API),授权成功后自动继续定位
  • showUserLocation(定位蓝点)同样先检查权限再激活(MapLibre LocationComponent 不会自动弹授权框)
  • 定位服务开关检测:GPS 与网络 provider 均关闭时立即报错,不再干等 10 秒超时
  • GPS 与网络双通道并发单次定位(先回先得):室内/无星历环境网络定位 1-3 秒内返回,修复仅请求 GPS 时冷启动数分钟无回调
  • 组件内置定位按钮增加即时反馈:点击显示「定位中...」,失败 toast 显示具体原因(权限拒绝/定位服务未开启/超时)
  • demo 页移除 uni.authorize 残留(uni-app x 无此 API,调用会直接报错)

⚡ 优化:Android 滑动卡顿

  • 底图/注记两个 raster 图层设置 raster-fade-duration: 0——MapLibre Style Spec 默认 300ms 淡入,快速滑动时新瓦片半透明过渡造成视觉卡糊,设为 0 瓦片即时显示

v1.5.3 🐛(2026-08)

🐛 Bug 修复(真机报错专项修复)

定位按钮报错 uni.authorize is not a function

  • uni-app x 不存在 uni.authorize API(仅存在于 uni-app Vue2/Vue3),组件内置定位按钮误调用导致 App Error
  • 修复:移除 uni.authorize,直接调用定位 API——uni-app x 首次调用定位会自动弹出系统授权框
  • ⚠️ 推翻 v1.5.0 中「demo 页增加 uni.authorize 运行时权限请求」的修复方案

UTS 回调报错 回调函数已释放,不能再次执行

  • HBuilderX 4.25+ 中 UTS 插件导出函数的回调参数默认触发一次后自动回收,二次触发即报错
  • 修复:四端(Android/iOS/鸿蒙/Web)10 个带回调的导出函数全部添加 @UTSJS.keepAlive 装饰器(initMap、setMarkerClickCallback、setMapLongClickCallback、setCameraChangeCallback、geocode_getLocation、geocode_getPoint、search、searchInBounds、searchNearby、getUserLocation)

搜索/地理编码报错 HTTP 请求失败: null

  • 原 Android 实现用 HttpURLConnection 在 UTS 调用线程(主线程)同步请求,触发 NetworkOnMainThreadException(message 为 null)
  • 修复:改用 uni.request 异步请求(框架自动调度网络线程执行)
  • 修复:res.data 类型处理——dataType 默认 json 时已是 UTSJSONObject,原 as string 强转抛转换异常,改为 typeof 分支兼容 string 与 UTSJSONObject

Web 端内联类型修复

  • Web 端残留 3 处内联对象字面量类型(违反 UTS 规范),统一为命名类型 MarkerInfo / TileLayerInfo / TdtLayerConfig

v1.5.2 🛡️(2026-08)

🛡️ 手势双重防御(真机双指缩放失效修复)

  • Android getMapAsync 回调中显式调用 setAllGesturesEnabled(true) 兜底防御手势被意外禁用,并输出手势状态日志(TDTMap: 手势状态 zoom=... scroll=...
  • 组件搜索框 .tdt-search-bar 显式设置 height: 70rpx + overflow: visible,防止 absolute 容器被拉伸撑满后覆盖整幅地图、拦截地图手势

v1.5.1 ⚙️(2026-08)

⚙️ 新增纹理渲染模式

  • 新增 textureMode prop(默认 false):Android 端通过 MapLibreMapOptions().textureMode(true) 启用纹理渲染模式

v1.5.0 🎉(2026-08)

🆕 新增:H5 / Web 平台支持

  • 新增 utssdk/web/index.uts(1087 行),基于 MapLibre GL JS 4.7.1 实现全部 58 个 API
  • 组件 tdt-map.uvue 增加 #ifdef WEB 条件编译,App 端使用 <native-view>,Web 端使用 <view> 容器
  • MapLibre GL JS 通过 unpkg CDN 在运行时动态加载(CSS + JS),无需额外构建配置
  • package.json 开启 H5-mobile 和 H5-pc 所有主流浏览器支持
  • config.uts 新增 webApiKey 字段,区分 App 端(服务端 Key)与 Web 端(浏览器端 Key)
技术细节 模块 Web 实现方式
底图 raster source + XYZ {x}{y}{z} tile URL,8 子域名
覆盖物 Marker(DOM)/ GeoJSON source + fill/line layer
事件 click / mousedown(长按) / moveend(相机)
定位 navigator.geolocation(需 HTTPS)
截图 canvas.toDataURL('image/png') → ArrayBuffer
搜索 fetch() → 天地图 REST API
坐标转换 纯算法 GCJ02→WGS84(与 App 端完全一致)

⚠️ Web 端限制:部分功能受浏览器环境制约——定位需 HTTPS、截图受跨域限制(Canvas taint)、TileJSON 等少数 API 不适用。

⚡ 性能优化

  • Android 缩放/滑动卡顿修复:style JSON 构建策略重写——从同时预建 6 个 RasterSource(vec/img/ter × 底图+注记)改为仅创建当前激活类型的 2 个 source,GPU 驻留瓦片数减少 67%,滑动显著流畅
  • switchMapType 改为重建样式(setStyle + fromJson)替代旧的 visibility 切换,切换时间 <100ms

🐛 Bug 修复

  • 修复:Android showUserLocation 无定位蓝点图标——MapLibre LocationComponent 默认不渲染图标,现用 GradientDrawable 程序化生成蓝色定位圆点(填充 + 白色边框 + 半透明精度圈)
  • 修复:Android 定位不生效——demo 页增加 uni.authorize({ scope: 'scope.userLocation' }) 运行时权限请求,Android 6+ 必须先弹窗授权才能启用定位

v1.4.0 🎉(2026-08)

🆕 新增功能(6 项)

地图交互增强

  • setMapLongClickCallback(cb) — 组件新增 @mapLongClick 事件,长按地图返回经纬度
  • setCameraChangeCallback(cb) — 组件新增 @cameraChange 事件,视野移动完成时触发

旋转与倾斜

  • setBearing(bearing) / getBearing() — 地图旋转(0°=正北,顺时针 0-360°)
  • setTilt(tilt) / getTilt() — 地图倾斜(0°=垂直俯瞰,范围 0-60°)

手势进阶

  • enableRotate(enabled) — 双指旋转手势开关
  • enableTilt(enabled) — 双指倾斜手势开关

技术实现

平台 旋转 (Bearing) 倾斜 (Tilt) 长按 相机事件
Android CameraPosition.Builder().bearing() CameraPosition.Builder().tilt() / setMaxPitchPreference addOnMapLongClickListener addOnCameraIdleListener
iOS MLNMapView.direction MLNMapCamera.pitch / pitchEnabled UILongPressGestureRecognizer mapViewCameraDidChange
鸿蒙 CameraPosition.bearing CameraPosition.tilt / setTiltGesturesEnabled mapLongClick 事件 cameraMove + cameraIdle 事件

⚠️ Android 倾斜手势限制:MapLibre Native Android 的 UiSettings 不提供 setTiltGesturesEnabled 方法(与高德/Google Maps 不同),通过 setMaxPitchPreference(0/60) 限制倾斜范围实现等效效果。


v1.2.0 ⚠️(2026-08)

🆕 新增功能(15 项)

覆盖物管理系统

  • add* 系列函数返回值改为 number(唯一 overlay ID)
  • removeOverlay(id) — 按 ID 移除单个覆盖物
  • showOverlay(id) — 按 ID 显示覆盖物
  • hideOverlay(id) — 按 ID 隐藏覆盖物

标记点击事件

  • setMarkerClickCallback(cb) — 组件新增 @markertap 事件

地图操作增强

  • checkResize() — 触发布局重绘
  • planBy(x, y) — 像素级平移
  • setViewport(points) — 多点自适应视野

信息计算

  • getDistance(start, end) — Haversine 距离计算(米)
  • getViewport(points) — 计算最佳中心点与缩放级别
  • getSize() — 获取容器像素尺寸
  • getCode() — 返回坐标系 EPSG:4326

天地图 Web API

  • geocode_getLocation — 逆地理编码(坐标 → 地址)
  • geocode_getPoint — 地理编码(地址 → 坐标)
  • search / searchInBounds / searchNearby — 关键字/范围/周边搜索

⚠️ 破坏性变更

函数 旧返回值 新返回值
addMarker void number
addPolygon void number
addPolyline void number
addCircle void number
addRectangle void number
addLabel void number
setMarkers void Array<number>

v1.1.0

  • 新增折线/圆/矩形/文字标注/批量标记/清除全部覆盖物
  • 手势控制(缩放/拖拽开关、最大/最小缩放限制)
  • 视野边界限制
  • GCJ02 → WGS84 坐标转换

v1.0.0

  • 初始版本
  • 矢/影像/地形三图切换
  • 标记点、多边形覆盖物
  • 地图类型切换、截图、生命周期

接口调用说明

本插件涉及两类接口:

1. 地图瓦片(前端直接请求)

矢量/影像/地形瓦片由各平台原生引擎(MapLibre / MapKit)直接从天地图瓦片服务器拉取,不经过任何后端。Key 通过 config.uts 或组件 apiKey 属性传入,拼入瓦片 URL 中。

2. 天地图 Web API(UTS 原生层直接请求)

搜索、地理编码等 API 由 UTS 原生层直接 HTTP 调用 api.tianditu.gov.cn,不走 uvue 前端运行时,也不经过用户后端。调用链路:

uvue 页面 → UTS 原生层 → HTTP → 天地图服务器

⚠️ 安全提示:Key 会随 App 包体分发,存在泄露风险。生产环境建议通过自有后端代理天地图 API,App 调用自己的后端接口,由后端转发并注入 Key,避免 Key 暴露在客户端。

3. 后端代理示例(各语言)

Node.js (Express)

const express = require('express')
const https = require('https')
const app = express()

const TDT_KEY = '你的天地图Key'
const TDT_HOST = 'api.tianditu.gov.cn'

app.get('/api/tdt/geocoder', (req, res) => {
  const params = new URLSearchParams(req.query)
  params.set('tk', TDT_KEY)
  https.get(`https://${TDT_HOST}/geocoder?${params}`, (resp) => {
    let data = ''
    resp.on('data', chunk => data += chunk)
    resp.on('end', () => res.json(JSON.parse(data)))
  })
})

app.get('/api/tdt/search', (req, res) => {
  const params = new URLSearchParams(req.query)
  params.set('tk', TDT_KEY)
  https.get(`https://${TDT_HOST}/search?${params}`, (resp) => {
    let data = ''
    resp.on('data', chunk => data += chunk)
    resp.on('end', () => res.json(JSON.parse(data)))
  })
})

Python (Flask)

from flask import Flask, request, jsonify
import requests

app = Flask(__name__)
TDT_KEY = '你的天地图Key'
TDT_HOST = 'https://api.tianditu.gov.cn'

@app.route('/api/tdt/<path:endpoint>')
def proxy(endpoint):
    params = dict(request.args)
    params['tk'] = TDT_KEY
    r = requests.get(f'{TDT_HOST}/{endpoint}', params=params)
    return jsonify(r.json())

Java (Spring Boot)

@RestController
@RequestMapping("/api/tdt")
public class TdtProxyController {

    private static final String TDT_KEY = "你的天地图Key";
    private static final String TDT_HOST = "https://api.tianditu.gov.cn";
    private final RestTemplate rest = new RestTemplate();

    @GetMapping("/{endpoint}")
    public Object proxy(@PathVariable String endpoint,
                        @RequestParam Map<String, String> params) {
        params.put("tk", TDT_KEY);
        String url = UriComponentsBuilder.fromHttpUrl(TDT_HOST + "/" + endpoint)
                .queryParams(params).toUriString();
        return rest.getForObject(url, Object.class);
    }
}

Go (net/http)

func tdtProxy(w http.ResponseWriter, r *http.Request, endpoint string) {
    q := r.URL.Query()
    q.Set("tk", "你的天地图Key")
    url := fmt.Sprintf("https://api.tianditu.gov.cn/%s?%s", endpoint, q.Encode())
    resp, _ := http.Get(url)
    defer resp.Body.Close()
    body, _ := io.ReadAll(resp.Body)
    w.Header().Set("Content-Type", "application/json")
    w.Write(body)
}

PHP

<?php
define('TDT_KEY', '你的天地图Key');
define('TDT_HOST', 'https://api.tianditu.gov.cn');

$endpoint = $_GET['_endpoint'] ?? 'geocoder';
unset($_GET['_endpoint']);
$_GET['tk'] = TDT_KEY;

$url = TDT_HOST . '/' . $endpoint . '?' . http_build_query($_GET);
echo file_get_contents($url);

App 端调用改为你自己的后端地址,后续如果 Key 泄露只需在后端轮换,无需重新发版。


更新日志

v1.5.3 🐛(最新)

  • 🐛 修复:定位按钮 uni.authorize is not a function——uni-app x 无此 API,移除后直接调定位(首次自动弹授权框,推翻 v1.5.0 的 uni.authorize 方案)
  • 🐛 修复:回调函数已释放,不能再次执行——四端 10 个带回调导出函数全部添加 @UTSJS.keepAlive 装饰器
  • 🐛 修复:HTTP 请求失败: null——Android 主线程同步 HttpURLConnection 改 uni.request 异步,并修复 res.data 类型处理
  • 🐛 修复:Web 端 3 处内联类型字面量 → 命名类型(MarkerInfo/TileLayerInfo/TdtLayerConfig)

v1.5.2 🛡️

  • 🛡️ 手势兜底:Android 显式 setAllGesturesEnabled(true) + 手势状态日志
  • 🐛 修复:搜索框 absolute 容器撑满拦截地图手势(height:70rpx + overflow:visible

v1.5.1 ⚙️

  • ✨ 新增:textureMode prop(Android 纹理渲染模式)

v1.5.0 🎉

  • ✨ 新增:H5 / Web 平台完整支持——新增 utssdk/web/index.uts(1087 行),基于 MapLibre GL JS 4.7.1 实现全部 58 个 API
  • ✨ 新增:Web Key 配置——config.uts 新增 webApiKey 字段,区分 App 端(服务端/Android 平台类型)与 Web 端(浏览器端类型)
  • ✨ 新增:组件 tdt-map.uvue 增加 #ifdef WEB 条件编译,App/Web 双路径无冲突
  • ⚡ 优化:Android 缩放/滑动卡顿修复——style JSON 从 6 RasterSource 减至 2 个,GPU 瓦片驻留减少 67%
  • ⚡ 优化:switchMapType 改为重建样式替代 visibility 切换,避免不可见 source 持续占用资源
  • 🐛 修复:Android showUserLocation 无定位蓝点图标——GradientDrawable 程序化生成蓝色定位圆点
  • 🐛 修复:Android 定位不生效——demo 页增加 uni.authorize 运行时权限请求

v1.4.0 🎉

  • ✨ 新增:地图交互增强——@mapLongClick 长按事件、@cameraChange 相机视野变化完成事件
  • ✨ 新增:旋转与倾斜——setBearing / getBearing / setTilt / getTilt,bearing 旋转(0-360°)、tilt 倾斜(0-60°)
  • ✨ 新增:手势进阶——enableRotate 双指旋转手势开关、enableTilt 双指倾斜手势开关
  • 🐛 修复:Android UiSettings.setTiltGesturesEnabled 不存在导致运行时崩溃(改用 setMaxPitchPreference
  • 🐛 修复:鸿蒙 cameraChange 事件不存在导致回调永不触发(改用 cameraMove + cameraIdle
  • 🐛 修复:iOS UILongPressGestureRecognizer 无 state 过滤导致单次长按多次触发
  • 🐛 修复:iOS mapView.camera nil 防御加固

v1.3.0 🎉

  • ✨ 新增:定位能力——showUserLocation 显示/隐藏用户位置蓝点,getUserLocation 获取当前 GPS 坐标(Android LocationManager / iOS CLLocationManager / 鸿蒙 geoLocationManager
  • ✨ 新增:信息窗口——showMarkerInfo / hideMarkerInfo 控制标记气泡弹窗,addMarker 增加 snippet 参数支持副标题
  • ✨ 新增:自定义瓦片图层——addCustomTileLayer / removeCustomTileLayer 叠加外部 XYZ 瓦片(如 OSM、天气、交通专题图),Android RasterSource+RasterLayer / iOS MLNRasterTileSource / 鸿蒙 addTileOverlay
  • 🐛 修复:Android searchNearby 函数缺失导致调用方报 undefined 错误
  • 🐛 修复:三端 destroyMap 内存泄漏(Android 4 项 / iOS 10 项 / 鸿蒙 6 项状态变量未清理)
  • 🐛 修复:iOS getUserLocation 未请求定位授权,iOS 14+ 定位永远静默失败
  • 🐛 修复:Android getUserLocation 无超时机制,GPS 关闭时回调永不触发
  • 🐛 修复:Android activateLocationComponentIfNeeded / showMarkerInfo NPE 风险(style 未加载或 mapLibreMap 为空时崩溃)
  • 🐛 修复:Android 标记点击回调永不触发——OnMapClickListener 返回 true 消费所有事件
  • 🐛 修复:iOS 标记点击崩溃——@objc 方法参数名 annotation 应为 didSelectAnnotation
  • 🐛 修复:iOS destroyMap 未 invalidate 定位轮询 NSTimer,销毁后仍持续运行
  • 🐛 修复:Android removeOverlay 未清理 markerList/polygonList 等类型列表,后续 clearMarkers 重复操作已移除对象
  • 🐛 修复:鸿蒙 clearOverlays 未同步清理 customTileOverlays Map
  • 🐛 修复:鸿蒙 addRectangle/setMarkers/clearOverlays/switchMapType 声明为 async 返回 Promise,导致 overlay ID 追踪断裂
  • 🐛 修复:鸿蒙自定义 urlEncode 用 UCS-2 编码中文(非 UTF-8),搜索含中文时 API 返回参数错误
  • 🐛 修复:三端天地图 Web API 统一改用 HTTPS(Android 9+ 默认拦截 HTTP 明文流量)

v1.2.1

  • 🐛 修复:HarmonyOS setMarkerClickCallback 重复注册 markerClick 监听器导致 @markertap 事件双重触发

v1.2.0

  • ✨ 新增:15 项功能——折线/圆/矩形覆盖物、文字标注、批量标记、覆盖物 ID 追踪系统(removeOverlay/showOverlay/hideOverlay)、标记点击事件 @markertapzoomIn/zoomOut/setMinZoom/setMaxZoompanTo/centerAndZoomenableZoom/enableDragsetMaxBoundsgetBounds、GCJ02→WGS84 坐标转换、checkResize/planBy/setViewport/getSize、Haversine 测距/最佳视野计算/坐标系查询、天地图地理编码/搜索 API

v1.1.0

  • ✨ 新增:地图类型切换、标记/多边形基础覆盖物、fitBounds/getCenter/getZoomsnapshot

v1.0.0

  • 🎉 初始版本:天地图矢量/影像/地形底图(MapLibre Android/iOS + 华为 MapKit 鸿蒙),三端统一 API

常见问题

Q: 瓦片不显示(灰白背景)?
A: 99% 是 Key 类型错误。请到 天地图控制台 创建「服务端」或「Android 平台」类型 Key,不要使用浏览器端 Key。

Q: HarmonyOS 截图报错?
A: 华为 MapKit 不支持 API 截图,snapshot() 返回空 ArrayBuffer 并回调错误。

Q: iOS 编译报 MLNMapView 找不到?
A: 确保 config.jsonpackage.json 中已声明 MapLibre iOS SDK 依赖。

隐私、权限声明

1. 本插件需要申请的系统权限列表:

Android: - android.permission.INTERNET(网络访问,加载天地图瓦片与调用天地图 Web API) - android.permission.ACCESS_NETWORK_STATE(网络状态检测) - android.permission.ACCESS_FINE_LOCATION / ACCESS_COARSE_LOCATION(定位权限,仅在使用定位功能时需要) iOS: - 定位权限 NSLocationWhenInUseUsageDescription(仅在使用定位功能时需要,由宿主工程 info.plist 配置) 鸿蒙: - ohos.permission.INTERNET(网络访问,由宿主工程 module.json5 声明) 说明:插件本身不申请相册、摄像头、麦克风等其它权限;不使用定位功能时无需申请定位权限。

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何用户个人信息、设备信息或行为数据。 网络请求仅用于地图功能本身,发送地址及用途如下: - 天地图瓦片服务器(t0-t7.tianditu.gov.cn):加载地图瓦片(矢量/影像/地形/注记),请求中仅携带用户自行配置的天地图 Key 与瓦片坐标参数 - 天地图 Web API(api.tianditu.gov.cn):地理编码、逆地理编码、地名搜索,请求中仅携带用户输入的搜索关键词与用户自行配置的天地图 Key - Web 端运行时从 unpkg.com 公共 CDN 加载 MapLibre GL JS 库文件(无需任何用户数据) 定位功能获取的坐标仅在本机显示蓝点使用,不上传至任何服务器。

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。