定位图标修改

This commit is contained in:
cwchen 2026-01-14 11:13:20 +08:00
parent eef16b07ae
commit 7ab1cda769
2 changed files with 294 additions and 81 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

View File

@ -7,6 +7,21 @@
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&&type=webgl&ak=iqyZkSZPurf61MhFV7hesbDukHdMBEEb"></script>
<script type="text/javascript" src="https://api.map.baidu.com/library/Convertor/1.2/src/Convertor_min.js"></script>
<script type="text/javascript">
// 确保Convertor库加载完成
window.baiduMapConvertorReady = false;
if (typeof BMapGL !== 'undefined' && typeof BMapGL.Convertor !== 'undefined') {
window.baiduMapConvertorReady = true;
} else {
// 监听Convertor库加载
setTimeout(function() {
if (typeof BMapGL !== 'undefined' && typeof BMapGL.Convertor !== 'undefined') {
window.baiduMapConvertorReady = true;
}
}, 1000);
}
</script>
<title>百度地图</title>
<style>
@ -393,6 +408,7 @@
watchPositionId: null,
watchOrientationId: null
},
locationIconCache: null,
elements: {
panel: null,
tree: null,
@ -502,6 +518,12 @@
return;
}
// 等待地图和Convertor库加载完成
if (!this.map) {
setTimeout(() => this.initLocationTracking(), 200);
return;
}
// 创建定位标记图标(带方向的箭头)
this.createLocationIcon();
@ -574,6 +596,111 @@
);
},
/**
* WGS84坐标转BD09坐标优先使用百度地图Web API最准确
*/
wgs84ToBd09(wgs_lng, wgs_lat, callback) {
// 优先使用Web API最准确
this.wgs84ToBd09WebAPI(wgs_lng, wgs_lat, callback);
},
/**
* 使用百度地图Web服务API进行坐标转换
*/
wgs84ToBd09WebAPI(wgs_lng, wgs_lat, callback) {
const ak = 'iqyZkSZPurf61MhFV7hesbDukHdMBEEb'; // 百度地图AK
const coords = `${wgs_lng},${wgs_lat}`;
const url = `https://api.map.baidu.com/geoconv/v1/?coords=${coords}&from=1&to=5&ak=${ak}`;
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.timeout = 5000;
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: point.x, lat: point.y };
console.log('Web API转换成功 - WGS84:', wgs_lng, wgs_lat, '-> BD09:', result.lng, result.lat);
if (callback) callback(result);
return;
} else {
console.warn('Web API返回错误:', response);
}
} catch (e) {
console.warn('Web API解析失败:', e);
}
}
// API调用失败使用备用算法
const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat);
console.log('使用备用算法转换 - WGS84:', wgs_lng, wgs_lat, '-> BD09:', result.lng, result.lat);
if (callback) callback(result);
}
};
xhr.onerror = () => {
console.warn('Web API网络错误使用备用算法');
const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat);
if (callback) callback(result);
};
xhr.ontimeout = () => {
console.warn('Web API超时使用备用算法');
const result = this.wgs84ToBd09Fallback(wgs_lng, wgs_lat);
if (callback) callback(result);
};
xhr.send();
},
/**
* WGS84转BD09算法精确版针对中国地区优化
*/
wgs84ToBd09Fallback(wgs_lng, wgs_lat) {
const x_PI = 3.14159265358979324 * 3000.0 / 180.0;
const PI = 3.1415926535897932384626;
const a = 6378245.0;
const ee = 0.00669342162296594323;
let x = Number(wgs_lng);
let y = Number(wgs_lat);
// WGS84转GCJ02火星坐标- 使用精确算法
let dlat = this.transformLat(x - 105.0, y - 35.0);
let dlng = this.transformLng(x - 105.0, y - 35.0);
let radlat = y / 180.0 * PI;
let magic = Math.sin(radlat);
magic = 1 - ee * magic * magic;
let sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
let mglat = y + dlat;
let mglng = x + dlng;
// GCJ02转BD09百度坐标- 使用精确算法
let z = Math.sqrt(mglng * mglng + mglat * mglat) + 0.00002 * Math.sin(mglat * x_PI);
let theta = Math.atan2(mglat, mglng) + 0.000003 * Math.cos(mglng * 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 };
},
transformLat(lng, lat) {
let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin(lat / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * Math.PI) + 320 * Math.sin(lat * Math.PI / 30.0)) * 2.0 / 3.0;
return ret;
},
transformLng(lng, lat) {
let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * Math.PI) + 20.0 * Math.sin(2.0 * lng * Math.PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * Math.PI) + 40.0 * Math.sin(lng / 3.0 * Math.PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * Math.PI) + 300.0 * Math.sin(lng / 30.0 * Math.PI)) * 2.0 / 3.0;
return ret;
},
/**
* 更新位置
*/
@ -581,56 +708,73 @@
const coords = position.coords || position;
const { latitude, longitude, heading, accuracy } = coords;
if (!latitude || !longitude) return;
// 将WGS84坐标转换为百度坐标
const point = new BMapGL.Point(longitude, latitude);
// 使用百度地图的坐标转换(如果需要)
// 注意BMapGL.Point 可以直接使用WGS84坐标但为了更准确建议转换
// 这里先直接使用,如果发现位置偏移,再添加坐标转换
// 保存当前位置(使用原始坐标,转换在显示时处理)
this.state.currentLocation = {
lat: latitude,
lng: longitude,
accuracy: accuracy || 0
};
// 如果有方向信息,更新方向
if (heading !== null && heading !== undefined) {
this.state.currentHeading = heading;
if (!latitude || !longitude) {
console.warn('位置信息无效:', { latitude, longitude });
return;
}
// 更新地图上的标记
this.updateLocationMarker();
console.log('收到位置更新 - WGS84:', longitude, latitude, '精度:', accuracy, '方向:', heading);
// 将WGS84坐标转换为百度BD09坐标使用官方API
this.wgs84ToBd09(longitude, latitude, (bd09) => {
if (!bd09) {
// 如果转换失败,使用备用算法
console.warn('坐标转换返回空,使用备用算法');
bd09 = this.wgs84ToBd09Fallback(longitude, latitude);
}
console.log('坐标转换完成 - BD09:', bd09.lng, bd09.lat);
// 保存转换后的百度坐标
this.state.currentLocation = {
lat: bd09.lat,
lng: bd09.lng,
accuracy: accuracy || 0
};
// 如果有方向信息,更新方向
if (heading !== null && heading !== undefined && !isNaN(heading)) {
const oldHeading = this.state.currentHeading;
this.state.currentHeading = heading;
if (Math.abs(heading - oldHeading) > 1) {
console.log('方向更新来自GPS:', heading);
}
}
// 更新地图上的标记
this.updateLocationMarker();
});
},
/**
* 更新定位标记
*/
updateLocationMarker() {
if (!this.map || !this.state.currentLocation) return;
if (!this.map || !this.state.currentLocation) {
console.warn('无法更新定位标记:地图或位置信息缺失');
return;
}
const { lng, lat } = this.state.currentLocation;
// 创建百度地图点(百度地图会自动处理坐标转换)
// 创建百度地图点(已转换为BD09坐标
const point = new BMapGL.Point(lng, lat);
// 创建带方向的图标
const icon = this.createRotatedLocationIcon(this.state.currentHeading);
// 如果标记已存在,更新位置和图标
if (this.overlays.locationMarker) {
this.map.removeOverlay(this.overlays.locationMarker);
}
// 创建新标记
this.overlays.locationMarker = new BMapGL.Marker(point, { icon });
this.map.addOverlay(this.overlays.locationMarker);
// 可选:自动跟随定位(取消注释以下代码以启用)
// this.map.setCenter(point);
console.log('更新定位标记 - 坐标:', lng, lat, '方向:', this.state.currentHeading);
// 创建带方向的图标(异步加载图片)
this.createRotatedLocationIcon(this.state.currentHeading).then((icon) => {
// 如果标记已存在,更新位置和图标
if (this.overlays.locationMarker) {
this.map.removeOverlay(this.overlays.locationMarker);
}
// 创建新标记
this.overlays.locationMarker = new BMapGL.Marker(point, { icon });
this.map.addOverlay(this.overlays.locationMarker);
}).catch((error) => {
console.error('创建定位图标失败:', error);
});
},
/**
@ -655,18 +799,22 @@
navigator.geolocation.getCurrentPosition(
(position) => {
const { latitude, longitude } = position.coords;
const point = new BMapGL.Point(longitude, latitude);
this.map.centerAndZoom(point, 18);
// 更新当前位置
this.state.currentLocation = {
lat: latitude,
lng: longitude,
accuracy: position.coords.accuracy || 0
};
// 更新标记
this.updateLocationMarker();
// 将WGS84坐标转换为百度BD09坐标
this.wgs84ToBd09(longitude, latitude, (bd09) => {
const point = new BMapGL.Point(bd09.lng, bd09.lat);
this.map.centerAndZoom(point, 18);
// 更新当前位置(使用转换后的坐标)
this.state.currentLocation = {
lat: bd09.lat,
lng: bd09.lng,
accuracy: position.coords.accuracy || 0
};
// 更新标记
this.updateLocationMarker();
});
},
(error) => {
console.warn('获取当前位置失败:', error);
@ -684,19 +832,72 @@
},
/**
* 创建旋转后的定位图标
* 创建旋转后的定位图标(使用 real_postion.png
*/
createRotatedLocationIcon(heading) {
// 如果图片已缓存直接使用返回Promise以保持接口一致性
if (this.locationIconCache && this.locationIconCache.img && this.locationIconCache.img.complete) {
return Promise.resolve(this.drawRotatedIcon(this.locationIconCache.img, heading));
}
// 创建图片对象
const img = new Image();
img.crossOrigin = 'anonymous';
// 使用 Promise 等待图片加载
return new Promise((resolve) => {
img.onload = () => {
// 缓存图片
this.locationIconCache = { img: img };
const icon = this.drawRotatedIcon(img, heading);
resolve(icon);
};
img.onerror = () => {
// 如果图片加载失败,使用默认图标(不旋转)
console.warn('real_postion.png 加载失败,使用默认图标');
const iconSize = 30;
const defaultIcon = new BMapGL.Icon('./image/real_postion.png', new BMapGL.Size(iconSize, iconSize), {
anchor: new BMapGL.Size(iconSize / 2, iconSize / 2)
});
resolve(defaultIcon);
};
// 加载图片
img.src = './image/real_postion.png';
});
},
/**
* 在Canvas上绘制旋转后的图标
*/
drawRotatedIcon(img, heading) {
// 获取图片原始尺寸
const imgWidth = img.naturalWidth || img.width || 30;
const imgHeight = img.naturalHeight || img.height || 30;
// 计算缩放比例保持宽高比最大尺寸为30
const maxSize = 30;
const scale = Math.min(maxSize / imgWidth, maxSize / imgHeight);
const displayWidth = imgWidth * scale;
const displayHeight = imgHeight * scale;
// Canvas尺寸需要足够大以容纳旋转后的图片对角线长度
const canvasSize = Math.ceil(Math.max(displayWidth, displayHeight) * Math.sqrt(2));
const canvas = document.createElement('canvas');
canvas.width = 40;
canvas.height = 40;
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext('2d');
// 设置高质量渲染
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// 保存上下文
ctx.save();
// 移动到中心点
ctx.translate(20, 20);
ctx.translate(canvasSize / 2, canvasSize / 2);
// 旋转画布heading是相对于正北的角度需要转换为相对于画布的角度
// 百度地图中heading是相对于正北的角度0-360顺时针为正
@ -704,35 +905,16 @@
const rotation = (heading - 90) * Math.PI / 180; // 转换为弧度并调整方向
ctx.rotate(rotation);
// 绘制圆形背景
ctx.fillStyle = '#3388ff';
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2);
ctx.fill();
// 绘制白色边框
ctx.strokeStyle = '#ffffff';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(0, 0, 15, 0, Math.PI * 2);
ctx.stroke();
// 绘制方向箭头(向上)
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(0, -15);
ctx.lineTo(-8, -5);
ctx.lineTo(8, -5);
ctx.closePath();
ctx.fill();
// 绘制图片(居中,保持原始宽高比)
ctx.drawImage(img, -displayWidth / 2, -displayHeight / 2, displayWidth, displayHeight);
// 恢复上下文
ctx.restore();
// 转换为图片URL
const iconUrl = canvas.toDataURL();
return new BMapGL.Icon(iconUrl, new BMapGL.Size(40, 40), {
anchor: new BMapGL.Size(20, 20)
return new BMapGL.Icon(iconUrl, new BMapGL.Size(canvasSize, canvasSize), {
anchor: new BMapGL.Size(canvasSize / 2, canvasSize / 2)
});
},
@ -743,30 +925,46 @@
// 绑定方法,确保在事件监听器中能正确访问 this
this.handleOrientation = this.handleOrientation.bind(this);
console.log('开始监听设备方向...');
// 检查是否支持 DeviceOrientationEvent
if (typeof DeviceOrientationEvent !== 'undefined' && typeof DeviceOrientationEvent.requestPermission === 'function') {
// iOS 13+ 需要请求权限
console.log('请求设备方向权限...');
DeviceOrientationEvent.requestPermission()
.then(response => {
if (response === 'granted') {
console.log('设备方向权限已授予');
window.addEventListener('deviceorientation', this.handleOrientation);
} else {
console.warn('设备方向权限被拒绝');
}
})
.catch(console.error);
.catch(error => {
console.error('请求设备方向权限失败:', error);
});
} else if (window.DeviceOrientationEvent) {
// 其他设备直接监听
console.log('直接监听设备方向事件');
window.addEventListener('deviceorientation', this.handleOrientation);
} else {
console.log('DeviceOrientationEvent不支持尝试使用uni-app Compass API');
// 如果不支持,尝试使用 Compass APIuni-app
if (typeof plus !== 'undefined' && plus.compass) {
this.state.watchOrientationId = plus.compass.watchHeading(
(heading) => {
this.state.currentHeading = heading.magneticHeading;
this.updateLocationMarker();
const newHeading = heading.magneticHeading;
if (Math.abs(newHeading - this.state.currentHeading) > 1) {
this.state.currentHeading = newHeading;
console.log('方向更新来自Compass:', newHeading);
this.updateLocationMarker();
}
},
(error) => console.error('获取方向失败:', error),
{ frequency: 100 }
);
} else {
console.warn('设备方向功能不可用');
}
}
},
@ -776,9 +974,24 @@
*/
handleOrientation(event) {
// alpha: 绕Z轴旋转指南针方向0-360度
if (event.alpha !== null && event.alpha !== undefined) {
this.state.currentHeading = event.alpha;
this.updateLocationMarker();
// 注意:某些设备可能使用不同的属性
let newHeading = null;
if (event.alpha !== null && event.alpha !== undefined && !isNaN(event.alpha)) {
newHeading = event.alpha;
} else if (event.webkitCompassHeading !== null && event.webkitCompassHeading !== undefined) {
// iOS Safari 可能使用 webkitCompassHeading
newHeading = event.webkitCompassHeading;
}
if (newHeading !== null && !isNaN(newHeading)) {
// 只有当方向变化超过1度时才更新避免频繁更新
const oldHeading = this.state.currentHeading;
if (Math.abs(newHeading - oldHeading) > 1 || oldHeading === 0) {
this.state.currentHeading = newHeading;
console.log('方向更新:', newHeading, '度');
this.updateLocationMarker();
}
}
},