概述
用 uniapp 开发APP,版本 vue3,HBuildX 4.75
map组件在 APP 平台(vue版)上,地图组件的点击事件无法直接返回经纬度。我们通过屏幕坐标和地图视野范围,计算出点击位置的经纬度。

核心思路
将屏幕上的点击位置,按照比例映射到地图的地理坐标上。
实现步骤
第一步:获取屏幕点击坐标
当用户点击地图时,我们从触摸事件中获取点击位置在屏幕上的坐标:
clickX:点击位置距离屏幕左边的像素距离
clickY:点击位置距离屏幕顶部的像素距离
第二步:转换为地图容器内的相对坐标
地图组件在屏幕上有一个固定的位置和大小。我们需要计算点击位置相对于地图容器的坐标:
1 2
| 相对X = 屏幕X - 地图容器左边距离 相对Y = 屏幕Y - 地图容器顶部距离
|
第三步:计算比例
将相对坐标转换为比例值(0 到 1 之间):
ratioX = 相对X / 地图容器宽度(0 = 最左边,1 = 最右边)
ratioY = 相对Y / 地图容器高度(0 = 最上边,1 = 最下边)
第四步:获取地图视野范围
通过地图 API 获取当前地图显示的地理范围:
- 西南角(左下角):
{ latitude: 39.91, longitude: 116.39 }
- 东北角(右上角):
{ latitude: 39.92, longitude: 116.41 }
第五步:按比例计算经纬度
使用线性插值,根据点击位置的比例,在地图视野范围内计算对应的经纬度:
经度计算(从左到右):
1
| 经度 = 西边经度 + (东边经度 - 西边经度) × ratioX
|
纬度计算(从下到上,注意屏幕Y轴和纬度方向相反):
1
| 纬度 = 南边纬度 + (北边纬度 - 南边纬度) × (1 - ratioY)
|
举例说明
假设:
- 屏幕点击位置:
(375, 500) 像素
- 地图容器位置:左边 32px,顶部 128px,宽度 686px,高度 822px
- 地图视野:西南角
(39.91, 116.39),东北角 (39.92, 116.41)
计算过程:
相对坐标:
relativeX = 375 - 32 = 343
relativeY = 500 - 128 = 372
比例:
ratioX = 343 / 686 = 0.5(水平中间)
ratioY = 372 / 822 = 0.453(垂直中偏上)
经纬度:
经度 = 116.39 + (116.41 - 116.39) × 0.5 = 116.40
纬度 = 39.91 + (39.92 - 39.91) × (1 - 0.453) = 39.91547
最终结果:点击位置的经纬度是 (39.91547, 116.40)
注意事项
精度:这种方法在小范围视野内(几公里)精度较好,误差通常在 10-50 米左右,对于围栏设置等场景足够使用。
纬度反转:屏幕坐标系 Y 轴向下增大,但地图纬度向上增大,所以计算纬度时需要使用 (1 - ratioY) 来反转。
线性插值:我们假设地图上两点之间的坐标变化是均匀的(线性),这在较小视野范围内是合理的近似。
总结
通过”屏幕坐标 → 相对坐标 → 比例 → 经纬度”的转换过程,我们实现了在 APP 平台上获取地图点击位置经纬度的功能。核心思想就是按比例映射:点击位置在地图上的相对位置,对应到地理坐标上的相对位置。
1 2 3 4 5 6 7 8 9
| <view class="outgoing-map-container" @tap="handleContainerTap" > <map :longitude="currentLongitude" :latitude="currentLatitude" :scale="currentScale" :markers="markers" :show-location="showLocation" :include-points="includePoints" :circles="circles" :polyline="polyline" :polygons="polygons" class="map" @tap="handleMapTap" @markertap="handleMarkerTap" @poitap="handleMapTap" :enable-zoom="true" :enable-scroll="true" id="map"></map> </view>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| handleContainerTap(e) { console.log('xxxx',e) if (!this.enableAppTapLocation) return if (!this.mapContext) { console.warn('地图上下文未初始化') return } let clickX, clickY if (e.detail && typeof e.detail.x === 'number' && typeof e.detail.y === 'number') { clickX = e.detail.x clickY = e.detail.y } else if (e.changedTouches && e.changedTouches.length > 0) { const touch = e.changedTouches[0] clickX = touch.clientX || touch.pageX clickY = touch.clientY || touch.pageY } if (typeof clickX === 'number' && typeof clickY === 'number') { this.processTapLocation(clickX, clickY) } else { console.warn('无法获取点击坐标,@tap 事件对象:', e) } }, processTapLocation(clickX, clickY) { this.mapContext.getRegion({ success: async (res) => { try { const coords = await this.screenToLatLng( clickX, clickY, res ) if (coords) { console.log('=== 地图点击坐标计算 ===') console.log('屏幕坐标:', { x: clickX, y: clickY }) console.log('地图视野范围:', res) console.log('计算的经纬度:', coords) this.$emit('map-tap', { detail: { latitude: coords.latitude, longitude: coords.longitude } }) } else { console.warn('坐标转换失败') } } catch (err) { console.error('处理地图点击失败', err) } }, fail: (err) => { console.error('获取地图视野范围失败', err) } }) }, screenToLatLng(screenX, screenY, region) { return new Promise((resolve) => { if (!region || !region.northeast || !region.southwest) { console.warn('地图视野范围数据无效', region) resolve(null) return } const query = uni.createSelectorQuery().in(this) query.select('.map').boundingClientRect((rect) => { if (!rect) { console.warn('获取地图容器位置失败') resolve(null) return } const relativeX = screenX - rect.left const relativeY = screenY - rect.top if (relativeX < 0 || relativeX > rect.width || relativeY < 0 || relativeY > rect.height) { console.warn('点击坐标超出地图范围', { relativeX, relativeY, width: rect.width, height: rect.height }) resolve(null) return } const ratioX = relativeX / rect.width const ratioY = relativeY / rect.height const sw = region.southwest const ne = region.northeast const lat = sw.latitude + (ne.latitude - sw.latitude) * (1 - ratioY) const lng = sw.longitude + (ne.longitude - sw.longitude) * ratioX if (typeof lat !== 'number' || typeof lng !== 'number' || isNaN(lat) || isNaN(lng) || lat < -90 || lat > 90 || lng < -180 || lng > 180) { console.warn('计算的坐标无效', { lat, lng }) resolve(null) return } resolve({ latitude: lat, longitude: lng }) }).exec() }) },
|