更新记录
v1.5.5(2026-08-13)
天地图 UTS 原生插件(uni-app x 蒸汽模式)
基于 MapLibre(Android/iOS/Web)与华为 MapKit TileOverlay(鸿蒙)的天地图原生地图插件,GPU 渲染,四端统一 API。
插件市场 ID:beige-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。
插件市场 ID:beige-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 |
快速开始
安装
- 在 uni-app x 项目根目录创建
uni_modules/beige-tdt-map/ - 将本插件所有文件放入该目录
- 配置 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(定位蓝点)同样先检查权限再激活(MapLibreLocationComponent不会自动弹授权框)- 定位服务开关检测: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.authorizeAPI(仅存在于 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)
⚙️ 新增纹理渲染模式
- 新增
textureModeprop(默认 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无定位蓝点图标——MapLibreLocationComponent默认不渲染图标,现用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 ⚙️
- ✨ 新增:
textureModeprop(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.cameranil 防御加固
v1.3.0 🎉
- ✨ 新增:定位能力——
showUserLocation显示/隐藏用户位置蓝点,getUserLocation获取当前 GPS 坐标(AndroidLocationManager/ iOSCLLocationManager/ 鸿蒙geoLocationManager) - ✨ 新增:信息窗口——
showMarkerInfo/hideMarkerInfo控制标记气泡弹窗,addMarker增加snippet参数支持副标题 - ✨ 新增:自定义瓦片图层——
addCustomTileLayer/removeCustomTileLayer叠加外部 XYZ 瓦片(如 OSM、天气、交通专题图),AndroidRasterSource+RasterLayer/ iOSMLNRasterTileSource/ 鸿蒙addTileOverlay - 🐛 修复:Android
searchNearby函数缺失导致调用方报 undefined 错误 - 🐛 修复:三端
destroyMap内存泄漏(Android 4 项 / iOS 10 项 / 鸿蒙 6 项状态变量未清理) - 🐛 修复:iOS
getUserLocation未请求定位授权,iOS 14+ 定位永远静默失败 - 🐛 修复:Android
getUserLocation无超时机制,GPS 关闭时回调永不触发 - 🐛 修复:Android
activateLocationComponentIfNeeded/showMarkerInfoNPE 风险(style 未加载或 mapLibreMap 为空时崩溃) - 🐛 修复:Android 标记点击回调永不触发——
OnMapClickListener返回true消费所有事件 - 🐛 修复:iOS 标记点击崩溃——
@objc方法参数名annotation应为didSelectAnnotation - 🐛 修复:iOS
destroyMap未 invalidate 定位轮询 NSTimer,销毁后仍持续运行 - 🐛 修复:Android
removeOverlay未清理markerList/polygonList等类型列表,后续clearMarkers重复操作已移除对象 - 🐛 修复:鸿蒙
clearOverlays未同步清理customTileOverlaysMap - 🐛 修复:鸿蒙
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)、标记点击事件@markertap、zoomIn/zoomOut/setMinZoom/setMaxZoom、panTo/centerAndZoom、enableZoom/enableDrag、setMaxBounds、getBounds、GCJ02→WGS84 坐标转换、checkResize/planBy/setViewport/getSize、Haversine 测距/最佳视野计算/坐标系查询、天地图地理编码/搜索 API
v1.1.0
- ✨ 新增:地图类型切换、标记/多边形基础覆盖物、
fitBounds/getCenter/getZoom、snapshot
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.json 或 package.json 中已声明 MapLibre iOS SDK 依赖。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 0
赞赏 0
下载 12504411
赞赏 1941
赞赏
京公网安备:11010802035340号