From a9c2b1dae01b60ed5d722fc33b972685d1eb5d35 Mon Sep 17 00:00:00 2001 From: cwchen <1048842385@qq.com> Date: Wed, 14 Jan 2026 13:29:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/static/map.html | 578 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 549 insertions(+), 29 deletions(-) diff --git a/src/static/map.html b/src/static/map.html index 7f3a50e..21ecd40 100644 --- a/src/static/map.html +++ b/src/static/map.html @@ -406,9 +406,12 @@ currentLocation: null, currentHeading: 0, watchPositionId: null, - watchOrientationId: null + watchOrientationId: null, + watchLocationTimer: null, + mapLoaded: false }, locationIconCache: null, + baiduGeolocation: null, elements: { panel: null, tree: null, @@ -429,15 +432,12 @@ this.state.projectInfo = Utils.safeParseJSON(params.projectInfo, []); this.state.currentClickedProject = Utils.safeParseJSON(params.clickedProject); - // 初始化地图 + // 初始化地图(地图加载完成后再初始化定位功能) this.initMap(this.state.projectInfo); // 渲染工程列表 this.renderProjectList(); - // 初始化定位功能 - this.initLocationTracking(); - // 处理 URL Action this.handleUrlActions(params); }, @@ -505,7 +505,27 @@ this.map.centerAndZoom(center, 12); this.map.enableScrollWheelZoom(true); - projectInfo.forEach(p => this.addProjectMarker(p)); + // 等待地图加载完成 + this.map.addEventListener('tilesloaded', () => { + console.log('地图瓦片加载完成'); + this.state.mapLoaded = true; + + // 地图加载完成后,添加项目标记 + projectInfo.forEach(p => this.addProjectMarker(p)); + + // 地图加载完成后,初始化定位跟踪 + this.initLocationTracking(); + }); + + // 也监听地图初始化完成事件(备用) + this.map.addEventListener('load', () => { + console.log('地图加载完成'); + if (!this.state.mapLoaded) { + this.state.mapLoaded = true; + projectInfo.forEach(p => this.addProjectMarker(p)); + this.initLocationTracking(); + } + }); }, /** @@ -575,27 +595,474 @@ * 开始监听位置变化 */ startWatchingPosition() { - const options = { - enableHighAccuracy: true, // 高精度定位 - timeout: 10000, - maximumAge: 0 // 不使用缓存 + // 优先使用百度地图SDK定位(直接返回BD09坐标,最准确) + if (typeof BMapGL !== 'undefined' && BMapGL.Geolocation) { + console.log('使用百度地图SDK辅助定位API(优先)'); + this.startBaiduGeolocation(); + } else if (typeof uni !== 'undefined' && uni.getLocation) { + console.log('使用uni-app GPS定位API(WGS84)'); + this.startUniAppGPSGeolocation(); + } else if (typeof plus !== 'undefined' && plus.geolocation) { + console.log('使用5+定位API'); + this.startPlusGeolocation(); + } else { + console.log('使用浏览器定位API'); + this.startBrowserGeolocation(); + } + }, + + /** + * 使用百度地图定位(返回BD09坐标,无需转换)- 开启SDK辅助定位 + */ + startBaiduGeolocation() { + // 确保地图已加载完成 + if (!this.map || !this.state.mapLoaded) { + console.warn('地图未加载完成,延迟启动百度定位'); + setTimeout(() => this.startBaiduGeolocation(), 500); + return; + } + + const geolocation = new BMapGL.Geolocation(); + // 开启SDK辅助定位,提高定位精度 + geolocation.enableSDKLocation(); + + console.log('已开启百度地图SDK辅助定位'); + + // 获取当前位置,增加超时时间等待GPS定位 + let retryCount = 0; + const maxRetries = 3; + + const tryGetPosition = () => { + geolocation.getCurrentPosition((result) => { + const status = geolocation.getStatus(); + console.log('百度地图定位状态:', status, '结果:', result, '重试次数:', retryCount); + + // 百度地图定位成功状态码是0 + if (status === 0 && result && result.point) { + const point = result.point; + const accuracy = result.accuracy || 999; // 如果没有精度信息,假设精度较差 + console.log('百度地图SDK定位成功 - 经度:', point.lng, '纬度:', point.lat, '精度:', accuracy, '米'); + + // 验证坐标有效性 + if (point.lng && point.lat && !isNaN(point.lng) && !isNaN(point.lat) && + Math.abs(point.lng) <= 180 && Math.abs(point.lat) <= 90) { + + // 如果精度太差(大于50米)且还有重试次数,继续重试 + if (accuracy > 50 && retryCount < maxRetries) { + retryCount++; + console.log(`精度较差(${accuracy}米),重试获取更精确位置 (${retryCount}/${maxRetries})`); + setTimeout(tryGetPosition, 2000); + return; + } + + // 如果精度仍然很差(大于100米),切换到GPS定位 + if (accuracy > 100 && typeof uni !== 'undefined' && uni.getLocation) { + console.warn('百度定位精度较差(' + accuracy + '米),切换到GPS定位'); + this.startUniAppGPSGeolocation(); + return; + } + + this.state.currentLocation = { + lng: point.lng, + lat: point.lat, + accuracy: accuracy + }; + + // 使用逆地理编码验证位置(可选,用于调试) + this.verifyLocationWithGeocoder(point.lng, point.lat); + + this.updateLocationMarker(); + } else { + console.error('百度定位返回无效坐标:', point); + // 降级到uni-app定位 + if (typeof uni !== 'undefined' && uni.getLocation) { + this.startUniAppGPSGeolocation(); + } + } + } else { + // 如果失败且还有重试次数,继续重试 + if (retryCount < maxRetries) { + retryCount++; + console.log(`定位失败,重试 (${retryCount}/${maxRetries})`); + setTimeout(tryGetPosition, 2000); + return; + } + + console.warn('百度地图定位失败,状态:', status, '尝试备用方案'); + // 降级到uni-app定位 + if (typeof uni !== 'undefined' && uni.getLocation) { + this.startUniAppGPSGeolocation(); + } else { + this.startBrowserGeolocation(); + } + } + }, { + enableHighAccuracy: true, + timeout: 30000 // 30秒超时,等待GPS定位 + }); }; + + tryGetPosition(); + + // 持续监听位置变化(使用定时器定期获取) + this.state.watchLocationTimer = setInterval(() => { + geolocation.getCurrentPosition((result) => { + const status = geolocation.getStatus(); + if (status === 0 && result && result.point) { + const point = result.point; + const accuracy = result.accuracy || 999; + + // 验证坐标有效性 + if (point.lng && point.lat && !isNaN(point.lng) && !isNaN(point.lat) && + Math.abs(point.lng) <= 180 && Math.abs(point.lat) <= 90) { + + // 如果精度太差(大于100米),切换到GPS定位 + if (accuracy > 100 && typeof uni !== 'undefined' && uni.getLocation) { + console.warn('百度定位精度持续较差(' + accuracy + '米),切换到GPS定位'); + clearInterval(this.state.watchLocationTimer); + this.startUniAppGPSGeolocation(); + return; + } + + // 只更新精度更好的位置(精度提升超过10米,或者精度小于50米) + if (!this.state.currentLocation || + (accuracy < this.state.currentLocation.accuracy - 10) || + (accuracy < 50 && this.state.currentLocation.accuracy >= 50)) { + + console.log('位置更新 - 经度:', point.lng, '纬度:', point.lat, '精度:', accuracy, '米'); + + this.state.currentLocation = { + lng: point.lng, + lat: point.lat, + accuracy: accuracy + }; + this.updateLocationMarker(); + } + } + } + }, { + enableHighAccuracy: true, + timeout: 30000 + }); + }, 5000); // 每5秒更新一次,给GPS更多时间稳定 + + // 保存geolocation实例,以便后续使用 + this.baiduGeolocation = geolocation; + }, + + /** + * 使用逆地理编码验证位置(用于调试) + */ + verifyLocationWithGeocoder(lng, lat) { + if (!this.map) return; + + const geocoder = new BMapGL.Geocoder(); + const point = new BMapGL.Point(lng, lat); + + geocoder.getLocation(point, (result) => { + if (result && result.address) { + console.log('位置验证 - 地址:', result.address); + } + }); + }, + + /** + * 使用uni-app GPS定位(WGS84坐标,最准确) + */ + startUniAppGPSGeolocation() { + console.log('使用uni-app GPS定位(WGS84)'); + // 先获取一次当前位置 + uni.getLocation({ + type: 'wgs84', // 使用WGS84坐标系(GPS原始坐标),然后转换为BD09 + altitude: false, + geocode: false, + highAccuracyExpireTime: 10000, // 增加等待时间,获取GPS定位 + success: (res) => { + console.log('uni-app GPS定位成功:', res.longitude, res.latitude, '精度:', res.accuracy); + // uni-app返回的是WGS84坐标,需要转换为BD09 + this.wgs84ToBd09(res.longitude, res.latitude, (bd09) => { + if (bd09 && bd09.lng && bd09.lat) { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: res.accuracy || 0 + }; + console.log('GPS坐标转换完成 - BD09:', bd09.lng, bd09.lat); + this.updateLocationMarker(); + } else { + console.error('GPS坐标转换失败,使用GCJ02定位'); + this.startUniAppGeolocation(); + } + }); + }, + fail: (err) => { + console.error('uni-app GPS定位失败:', err); + // 降级到GCJ02定位 + this.startUniAppGeolocation(); + } + }); + + // 持续监听位置变化 + this.state.watchLocationTimer = setInterval(() => { + uni.getLocation({ + type: 'wgs84', + altitude: false, + geocode: false, + highAccuracyExpireTime: 10000, + success: (res) => { + this.wgs84ToBd09(res.longitude, res.latitude, (bd09) => { + if (bd09 && bd09.lng && bd09.lat) { + // 只更新精度更好的位置 + if (!this.state.currentLocation || res.accuracy < this.state.currentLocation.accuracy || res.accuracy < 20) { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: res.accuracy || 0 + }; + this.updateLocationMarker(); + } + } + }); + }, + fail: (err) => { + console.error('uni-app GPS定位更新失败:', err); + } + }); + }, 3000); // 每3秒更新一次,给GPS更多时间 + }, + + /** + * 使用uni-app定位(GCJ02坐标) + */ + startUniAppGeolocation() { + // 先获取一次当前位置 + uni.getLocation({ + type: 'gcj02', // 使用GCJ02坐标系(火星坐标),然后转换为BD09 + altitude: false, + geocode: false, + highAccuracyExpireTime: 4000, + success: (res) => { + console.log('uni-app定位成功:', res.longitude, res.latitude, '精度:', res.accuracy); + // uni-app返回的是GCJ02坐标,需要转换为BD09 + this.gcj02ToBd09(res.longitude, res.latitude, (bd09) => { + if (bd09 && bd09.lng && bd09.lat) { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: res.accuracy || 0 + }; + console.log('坐标转换完成 - BD09:', bd09.lng, bd09.lat); + this.updateLocationMarker(); + } else { + console.error('坐标转换失败,使用备用方案'); + // 如果转换失败,尝试使用百度地图定位 + if (typeof BMapGL !== 'undefined' && BMapGL.Geolocation) { + this.startBaiduGeolocation(); + } else { + this.startBrowserGeolocation(); + } + } + }); + }, + fail: (err) => { + console.error('uni-app定位失败:', err); + // 降级到百度地图定位或浏览器定位 + if (typeof BMapGL !== 'undefined' && BMapGL.Geolocation) { + this.startBaiduGeolocation(); + } else { + this.startBrowserGeolocation(); + } + } + }); + + // 持续监听位置变化 + this.state.watchLocationTimer = setInterval(() => { + uni.getLocation({ + type: 'gcj02', + altitude: false, + geocode: false, + highAccuracyExpireTime: 4000, + success: (res) => { + this.gcj02ToBd09(res.longitude, res.latitude, (bd09) => { + if (bd09 && bd09.lng && bd09.lat) { + // 只更新精度更好的位置 + if (!this.state.currentLocation || res.accuracy < this.state.currentLocation.accuracy || res.accuracy < 50) { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: res.accuracy || 0 + }; + this.updateLocationMarker(); + } + } + }); + }, + fail: (err) => { + console.error('uni-app定位更新失败:', err); + } + }); + }, 2000); // 每2秒更新一次位置 + }, + + /** + * 使用5+定位 + */ + startPlusGeolocation() { + const geolocation = plus.geolocation; + const options = { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 0 + }; + + // 获取当前位置 + geolocation.getCurrentPosition( + (position) => { + console.log('5+定位成功:', position); + // 5+返回的是WGS84坐标,需要转换为BD09 + this.wgs84ToBd09(position.coords.longitude, position.coords.latitude, (bd09) => { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: position.coords.accuracy || 0 + }; + this.updateLocationMarker(); + }); + }, + (error) => { + console.error('5+定位失败:', error); + this.startBrowserGeolocation(); + }, + options + ); + + // 持续监听位置变化 + this.state.watchPositionId = geolocation.watchPosition( + (position) => { + this.wgs84ToBd09(position.coords.longitude, position.coords.latitude, (bd09) => { + this.state.currentLocation = { + lat: bd09.lat, + lng: bd09.lng, + accuracy: position.coords.accuracy || 0 + }; + this.updateLocationMarker(); + }); + }, + (error) => { + console.error('5+定位监听错误:', error); + }, + options + ); + }, + + /** + * 使用浏览器定位 + */ + startBrowserGeolocation() { + if (!navigator.geolocation) { + console.error('浏览器不支持定位'); + return; + } + + const options = { + enableHighAccuracy: true, // 高精度定位(使用GPS) + timeout: 20000, // 增加超时时间到20秒,确保获取高精度定位 + maximumAge: 0 // 不使用缓存,始终获取最新位置 + }; + + console.log('开始浏览器定位,选项:', options); // 先获取一次当前位置 navigator.geolocation.getCurrentPosition( - (position) => this.updateLocation(position), - (error) => this.handleLocationError(error), + (position) => { + console.log('获取到初始位置:', position.coords); + this.updateLocation(position); + }, + (error) => { + console.error('获取初始位置失败:', error); + this.handleLocationError(error); + }, options ); // 持续监听位置变化 this.state.watchPositionId = navigator.geolocation.watchPosition( - (position) => this.updateLocation(position), - (error) => this.handleLocationError(error), + (position) => { + console.log('位置更新:', position.coords); + this.updateLocation(position); + }, + (error) => { + console.error('位置监听错误:', error); + this.handleLocationError(error); + }, options ); }, + /** + * GCJ02坐标转BD09坐标(百度坐标) + */ + gcj02ToBd09(gcj_lng, gcj_lat, callback) { + const ak = 'iqyZkSZPurf61MhFV7hesbDukHdMBEEb'; + // 使用6位小数精度(百度API推荐精度) + const coords = `${Number(gcj_lng).toFixed(6)},${Number(gcj_lat).toFixed(6)}`; + const url = `https://api.map.baidu.com/geoconv/v1/?coords=${coords}&from=3&to=5&ak=${ak}`; + + console.log('GCJ02转BD09 - 输入:', gcj_lng, gcj_lat); + + const xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); + xhr.timeout = 10000; // 10秒超时 + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + try { + const response = JSON.parse(xhr.responseText); + if (response.status === 0 && response.result && response.result.length > 0) { + const point = response.result[0]; + const result = { + lng: Number(point.x), + lat: Number(point.y) + }; + console.log('GCJ02转BD09成功:', result.lng, result.lat); + if (callback) callback(result); + return; + } + } catch (e) { + console.error('GCJ02转BD09解析失败:', e); + } + } + // API失败,使用算法转换 + console.warn('GCJ02转BD09 API失败,使用算法转换'); + const result = this.gcj02ToBd09Fallback(gcj_lng, gcj_lat); + if (callback) callback(result); + } + }; + xhr.onerror = () => { + console.warn('GCJ02转BD09网络错误,使用算法转换'); + const result = this.gcj02ToBd09Fallback(gcj_lng, gcj_lat); + if (callback) callback(result); + }; + xhr.ontimeout = () => { + console.warn('GCJ02转BD09超时,使用算法转换'); + const result = this.gcj02ToBd09Fallback(gcj_lng, gcj_lat); + if (callback) callback(result); + }; + xhr.send(); + }, + + /** + * GCJ02转BD09算法 + */ + gcj02ToBd09Fallback(gcj_lng, gcj_lat) { + const x_PI = 3.14159265358979324 * 3000.0 / 180.0; + let z = Math.sqrt(gcj_lng * gcj_lng + gcj_lat * gcj_lat) + 0.00002 * Math.sin(gcj_lat * x_PI); + let theta = Math.atan2(gcj_lat, gcj_lng) + 0.000003 * Math.cos(gcj_lng * x_PI); + let bd_lng = z * Math.cos(theta) + 0.0065; + let bd_lat = z * Math.sin(theta) + 0.006; + return { lng: bd_lng, lat: bd_lat }; + }, + /** * WGS84坐标转BD09坐标(优先使用百度地图Web API,最准确) */ @@ -609,43 +1076,73 @@ */ wgs84ToBd09WebAPI(wgs_lng, wgs_lat, callback) { const ak = 'iqyZkSZPurf61MhFV7hesbDukHdMBEEb'; // 百度地图AK - const coords = `${wgs_lng},${wgs_lat}`; + // 使用6位小数精度(百度API推荐精度) + const coords = `${Number(wgs_lng).toFixed(6)},${Number(wgs_lat).toFixed(6)}`; const url = `https://api.map.baidu.com/geoconv/v1/?coords=${coords}&from=1&to=5&ak=${ak}`; + console.log('调用坐标转换API - WGS84:', wgs_lng, wgs_lat, 'URL:', url); + const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); - xhr.timeout = 5000; + xhr.timeout = 10000; // 10秒超时 xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200) { try { - const response = JSON.parse(xhr.responseText); + const responseText = xhr.responseText; + console.log('坐标转换API原始响应:', responseText); + const response = JSON.parse(responseText); + if (response.status === 0 && response.result && response.result.length > 0) { const point = response.result[0]; - const result = { lng: point.x, lat: point.y }; + // 百度API返回:x=经度,y=纬度 + const result = { + lng: Number(point.x), + lat: Number(point.y) + }; + + // 验证转换结果的有效性 + if (isNaN(result.lng) || isNaN(result.lat) || + result.lng === 0 || result.lat === 0 || + Math.abs(result.lng) > 180 || Math.abs(result.lat) > 90) { + console.error('坐标转换结果无效:', result, '原始响应:', response); + const fallback = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat); + console.warn('使用备用算法:', fallback); + if (callback) callback(fallback); + return; + } + + // 计算偏移距离(用于调试) + const offsetLng = result.lng - wgs_lng; + const offsetLat = result.lat - wgs_lat; console.log('Web API转换成功 - WGS84:', wgs_lng, wgs_lat, '-> BD09:', result.lng, result.lat); + console.log('坐标偏移:', '经度:', offsetLng.toFixed(6), '纬度:', offsetLat.toFixed(6)); + if (callback) callback(result); return; } else { - console.warn('Web API返回错误:', response); + console.error('Web API返回错误 - 状态码:', response.status, '错误信息:', response.message || JSON.stringify(response)); } } catch (e) { - console.warn('Web API解析失败:', e); + console.error('Web API解析失败:', e, '响应文本:', xhr.responseText); } + } else { + console.error('Web API请求失败 - HTTP状态码:', xhr.status, '响应:', xhr.responseText); } // API调用失败,使用备用算法 + console.warn('坐标转换API失败,使用备用算法'); const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat); - console.log('使用备用算法转换 - WGS84:', wgs_lng, wgs_lat, '-> BD09:', result.lng, result.lat); + console.log('备用算法转换结果 - WGS84:', wgs_lng, wgs_lat, '-> BD09:', result.lng, result.lat); if (callback) callback(result); } }; xhr.onerror = () => { - console.warn('Web API网络错误,使用备用算法'); + console.error('Web API网络错误'); const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat); if (callback) callback(result); }; xhr.ontimeout = () => { - console.warn('Web API超时,使用备用算法'); + console.error('Web API请求超时'); const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat); if (callback) callback(result); }; @@ -750,8 +1247,14 @@ * 更新定位标记 */ updateLocationMarker() { - if (!this.map || !this.state.currentLocation) { - console.warn('无法更新定位标记:地图或位置信息缺失'); + // 确保地图已加载完成且有位置信息 + if (!this.map || !this.state.mapLoaded) { + console.warn('无法更新定位标记:地图未加载完成'); + return; + } + + if (!this.state.currentLocation) { + console.warn('无法更新定位标记:位置信息缺失'); return; } @@ -781,8 +1284,8 @@ * 回到平板当前位置 */ centerToLocation() { - if (!this.map) { - console.warn('地图未初始化'); + if (!this.map || !this.state.mapLoaded) { + console.warn('地图未加载完成'); return; } @@ -1021,9 +1524,24 @@ * 停止定位跟踪 */ stopLocationTracking() { + // 停止定位定时器(包括uni-app和百度地图) + if (this.state.watchLocationTimer !== null) { + clearInterval(this.state.watchLocationTimer); + this.state.watchLocationTimer = null; + } + + // 清理百度地图定位实例 + if (this.baiduGeolocation) { + this.baiduGeolocation = null; + } + // 停止位置监听 if (this.state.watchPositionId !== null) { - navigator.geolocation.clearWatch(this.state.watchPositionId); + if (navigator.geolocation) { + navigator.geolocation.clearWatch(this.state.watchPositionId); + } else if (typeof plus !== 'undefined' && plus.geolocation) { + plus.geolocation.clearWatch(this.state.watchPositionId); + } this.state.watchPositionId = null; } @@ -1034,7 +1552,9 @@ } // 移除方向事件监听 - window.removeEventListener('deviceorientation', this.handleOrientation); + if (this.handleOrientation) { + window.removeEventListener('deviceorientation', this.handleOrientation); + } // 移除定位标记 if (this.overlays.locationMarker && this.map) {