定位图标修改
This commit is contained in:
parent
eef16b07ae
commit
7ab1cda769
Binary file not shown.
|
After Width: | Height: | Size: 831 B |
|
|
@ -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 API(uni-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();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue