微信小程序GPS坐标查询开发记录 版本v0.0.5

微信小程序GPS坐标查询开发记录 版本v0.0.5
ytkz背景
我开发了一款微信小程序,专注GPS坐标查询。功能包括地图点击获取经纬度、地名搜索定位、坐标格式转换(小数度/度分秒)、坐标分享及用户定位。
初期使用OpenStreetMap的Nominatim API进行地名搜索,但因国内访问不稳定,切换到高德地图地理编码API,提升了搜索效率。
实现要点
1.地图交互
使用微信小程序的
2.地名搜索
通过高德地图API实现地名搜索,返回坐标,直接适配地图组件,响应快,覆盖广。
3.坐标格式转换
支持小数度和度分秒格式,coord-convert.js 模块处理WGS84与GCJ02转换及格式切换。
/**
* 坐标转换工具类
* 移植自Python版本的coord_convert.py
*/
class CoordConverter {
constructor() {
this.x_pi = Math.PI * 3000.0 / 180.0;
this.a = 6378245.0; // 长半轴
this.ee = 0.00669342162296594323; // 扁率
}
/**
* 火星坐标系(GCJ-02)转百度坐标系(BD-09)
* 谷歌、高德——>百度
* @param {number} lng 火星坐标经度
* @param {number} lat 火星坐标纬度
* @returns {Array} [bd_lng, bd_lat]
*/
gcj02ToBd09(lng, lat) {
const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * this.x_pi);
const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * this.x_pi);
const bd_lng = z * Math.cos(theta) + 0.0065;
const bd_lat = z * Math.sin(theta) + 0.006;
return [bd_lng, bd_lat];
}
/**
* 百度坐标系(BD-09)转火星坐标系(GCJ-02)
* 百度——>谷歌、高德
* @param {number} bd_lon 百度坐标经度
* @param {number} bd_lat 百度坐标纬度
* @returns {Array} [gg_lng, gg_lat]
*/
bd09ToGcj02(bd_lon, bd_lat) {
const x = bd_lon - 0.0065;
const y = bd_lat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * this.x_pi);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * this.x_pi);
const gg_lng = z * Math.cos(theta);
const gg_lat = z * Math.sin(theta);
return [gg_lng, gg_lat];
}
/**
* WGS84转GCJ02(火星坐标系)
* @param {number} lng WGS84坐标系的经度
* @param {number} lat WGS84坐标系的纬度
* @returns {Array} [mglng, mglat]
*/
wgs84ToGcj02(lng, lat) {
if (this.outOfChina(lng, lat)) {
return [lng, lat];
}
let dlat = this._transformLat(lng - 105.0, lat - 35.0);
let dlng = this._transformLng(lng - 105.0, lat - 35.0);
const radlat = lat / 180.0 * Math.PI;
let magic = Math.sin(radlat);
magic = 1 - this.ee * magic * magic;
const sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtmagic) * Math.PI);
dlng = (dlng * 180.0) / (this.a / sqrtmagic * Math.cos(radlat) * Math.PI);
const mglat = lat + dlat;
const mglng = lng + dlng;
return [mglng, mglat];
}
/**
* GCJ02(火星坐标系)转GPS84
* @param {number} lng 火星坐标系的经度
* @param {number} lat 火星坐标系纬度
* @returns {Array} [lng, lat]
*/
gcj02ToWgs84(lng, lat) {
if (this.outOfChina(lng, lat)) {
return [lng, lat];
}
let dlat = this._transformLat(lng - 105.0, lat - 35.0);
let dlng = this._transformLng(lng - 105.0, lat - 35.0);
const radlat = lat / 180.0 * Math.PI;
let magic = Math.sin(radlat);
magic = 1 - this.ee * magic * magic;
const sqrtmagic = Math.sqrt(magic);
dlat = (dlat * 180.0) / ((this.a * (1 - this.ee)) / (magic * sqrtmagic) * Math.PI);
dlng = (dlng * 180.0) / (this.a / sqrtmagic * Math.cos(radlat) * Math.PI);
const mglat = lat + dlat;
const mglng = lng + dlng;
return [lng * 2 - mglng, lat * 2 - mglat];
}
/**
* 百度坐标系转WGS84
* @param {number} bd_lon 百度坐标经度
* @param {number} bd_lat 百度坐标纬度
* @returns {Array} [lng, lat]
*/
bd09ToWgs84(bd_lon, bd_lat) {
const [lon, lat] = this.bd09ToGcj02(bd_lon, bd_lat);
return this.gcj02ToWgs84(lon, lat);
}
/**
* WGS84转百度坐标系
* @param {number} lon WGS84经度
* @param {number} lat WGS84纬度
* @returns {Array} [bd_lng, bd_lat]
*/
wgs84ToBd09(lon, lat) {
const [lng, lat_] = this.wgs84ToGcj02(lon, lat);
return this.gcj02ToBd09(lng, lat_);
}
/**
* 纬度转换辅助函数
* @private
*/
_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));
const sin_lng_pi = Math.sin(6.0 * lng * Math.PI);
const sin_2_lng_pi = Math.sin(2.0 * lng * Math.PI);
const sin_lat_pi = Math.sin(lat * Math.PI);
const sin_lat_3_pi = Math.sin(lat / 3.0 * Math.PI);
const sin_lat_12_pi = Math.sin(lat / 12.0 * Math.PI);
const sin_lat_30_pi = Math.sin(lat * Math.PI / 30.0);
ret += (20.0 * sin_lng_pi + 20.0 * sin_2_lng_pi) * 2.0 / 3.0;
ret += (20.0 * sin_lat_pi + 40.0 * sin_lat_3_pi) * 2.0 / 3.0;
ret += (160.0 * sin_lat_12_pi + 320 * sin_lat_30_pi) * 2.0 / 3.0;
return ret;
}
/**
* 经度转换辅助函数
* @private
*/
_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;
}
/**
* 判断是否在国内,不在国内不做偏移
* @param {number} lng 经度
* @param {number} lat 纬度
* @returns {boolean} 是否在国外
*/
outOfChina(lng, lat) {
return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}
/**
* 批量转换坐标数组
* @param {Array} coordinates 坐标数组 [[lng, lat], ...]
* @param {string} fromType 源坐标系类型 ('gcj02', 'bd09', 'wgs84')
* @param {string} toType 目标坐标系类型 ('gcj02', 'bd09', 'wgs84')
* @returns {Array} 转换后的坐标数组
*/
batchConvert(coordinates, fromType, toType) {
if (fromType === toType) {
return coordinates;
}
const convertMap = {
'gcj02_bd09': this.gcj02ToBd09.bind(this),
'bd09_gcj02': this.bd09ToGcj02.bind(this),
'wgs84_gcj02': this.wgs84ToGcj02.bind(this),
'gcj02_wgs84': this.gcj02ToWgs84.bind(this),
'bd09_wgs84': this.bd09ToWgs84.bind(this),
'wgs84_bd09': this.wgs84ToBd09.bind(this)
};
const convertKey = `${fromType}_${toType}`;
const convertFunc = convertMap[convertKey];
if (!convertFunc) {
throw new Error(`不支持的坐标转换: ${fromType} -> ${toType}`);
}
return coordinates.map(coord => {
if (Array.isArray(coord[0])) {
// 处理多边形坐标
return coord.map(point => convertFunc(point[0], point[1]));
} else {
// 处理单个坐标点
return convertFunc(coord[0], coord[1]);
}
});
}
}
// 创建全局实例
window.coordConverter = new CoordConverter();
// 导出类(如果使用模块系统)
if (typeof module !== 'undefined' && module.exports) {
module.exports = CoordConverter;
}
4.坐标输入与分享
支持手动输入坐标(两种格式),自动识别剪贴板数据。分享功能通过 onShareAppMessage 传递坐标。
5. 用户定位
调用 wx.getLocation 获取位置,处理权限逻辑,拒绝授权时回退到默认位置(广州)。
遇到的问题
- OpenStreetMap访问慢:切换到高德地图API,申请免费密钥。
- 坐标系差异:开发转换模块,确保显示与输入一致。
- 输入格式多样:支持小数度和度分秒,添加严格验证。
- 权限管理:引导用户开启位置权限,失败时使用默认位置。
总结
小程序现已稳定运行,满足坐标查询需求,高德地图API提升了搜索体验。未来计划支持多结果展示、历史记录、多语言及性能优化。欢迎体验并提出建议!