590 lines
19 KiB
JavaScript
590 lines
19 KiB
JavaScript
// 地图工具函数 - 内联腾讯地图SDK核心代码
|
|
|
|
// 腾讯地图Key
|
|
const QQMAP_KEY = 'RO5BZ-ECZ63-7US3C-RT5QW-TIDZE-2FF35';
|
|
|
|
// 内联腾讯地图SDK核心代码
|
|
const QQMapWX = (function() {
|
|
// 错误配置
|
|
const ERROR_CONF = {
|
|
KEY_ERR: 311,
|
|
KEY_ERR_MSG: 'key格式错误',
|
|
PARAM_ERR: 310,
|
|
PARAM_ERR_MSG: '请求参数信息有误',
|
|
SYSTEM_ERR: 600,
|
|
SYSTEM_ERR_MSG: '系统错误',
|
|
WX_ERR_CODE: 1000,
|
|
WX_OK_CODE: 200
|
|
};
|
|
|
|
// API基础URL
|
|
const BASE_URL = 'https://apis.map.qq.com/ws/';
|
|
const URL_SEARCH = BASE_URL + 'place/v1/search';
|
|
const URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
|
|
const URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
|
|
const URL_DISTANCE = BASE_URL + 'distance/v1/';
|
|
|
|
// 工具函数
|
|
const Utils = {
|
|
// 获取location参数
|
|
getLocationParam(location) {
|
|
if (typeof location == 'string') {
|
|
const locationArr = location.split(',');
|
|
if (locationArr.length === 2) {
|
|
location = {
|
|
latitude: location.split(',')[0],
|
|
longitude: location.split(',')[1]
|
|
};
|
|
} else {
|
|
location = {};
|
|
}
|
|
}
|
|
return location;
|
|
},
|
|
|
|
// 验证location值
|
|
checkLocation(param) {
|
|
const location = this.getLocationParam(param.location);
|
|
if (!location || !location.latitude || !location.longitude) {
|
|
const errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
|
|
param.fail(errconf);
|
|
param.complete(errconf);
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
// 构造错误数据结构
|
|
buildErrorConfig(errCode, errMsg) {
|
|
return {
|
|
status: errCode,
|
|
message: errMsg
|
|
};
|
|
},
|
|
|
|
// 回调函数默认处理
|
|
polyfillParam(param) {
|
|
param.success = param.success || function () { };
|
|
param.fail = param.fail || function () { };
|
|
param.complete = param.complete || function () { };
|
|
},
|
|
|
|
// 处理用户参数是否传入坐标进行不同的处理
|
|
locationProcess(param, locationsuccess, locationfail, locationcomplete) {
|
|
const that = this;
|
|
locationfail = locationfail || function (res) {
|
|
res.statusCode = ERROR_CONF.WX_ERR_CODE;
|
|
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
|
|
};
|
|
locationcomplete = locationcomplete || function (res) {
|
|
if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
|
|
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
|
|
}
|
|
};
|
|
if (!param.location) {
|
|
wx.getLocation({
|
|
type: 'gcj02',
|
|
success: locationsuccess,
|
|
fail: locationfail,
|
|
complete: locationcomplete
|
|
});
|
|
} else if (that.checkLocation(param)) {
|
|
const location = Utils.getLocationParam(param.location);
|
|
locationsuccess(location);
|
|
}
|
|
},
|
|
|
|
// 构造微信请求参数
|
|
buildWxRequestConfig(param, options, feature) {
|
|
const that = this;
|
|
options.header = { "content-type": "application/json" };
|
|
options.method = 'GET';
|
|
options.success = function (res) {
|
|
const data = res.data;
|
|
if (data.status === 0) {
|
|
that.handleData(param, data, feature);
|
|
} else {
|
|
param.fail(data);
|
|
}
|
|
};
|
|
options.fail = function (res) {
|
|
res.statusCode = ERROR_CONF.WX_ERR_CODE;
|
|
param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
|
|
};
|
|
options.complete = function (res) {
|
|
const statusCode = +res.statusCode;
|
|
switch(statusCode) {
|
|
case ERROR_CONF.WX_ERR_CODE: {
|
|
param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
|
|
break;
|
|
}
|
|
case ERROR_CONF.WX_OK_CODE: {
|
|
const data = res.data;
|
|
if (data.status === 0) {
|
|
param.complete(data);
|
|
} else {
|
|
param.complete(that.buildErrorConfig(data.status, data.message));
|
|
}
|
|
break;
|
|
}
|
|
default:{
|
|
param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
|
|
}
|
|
}
|
|
};
|
|
return options;
|
|
},
|
|
|
|
// 数据处理函数
|
|
handleData(param, data, feature) {
|
|
if (feature == 'search') {
|
|
const searchResult = data.data;
|
|
const searchSimplify = [];
|
|
for (let i = 0; i < searchResult.length; i++) {
|
|
searchSimplify.push({
|
|
id: searchResult[i].id || null,
|
|
title: searchResult[i].title || null,
|
|
latitude: searchResult[i].location && searchResult[i].location.lat || null,
|
|
longitude: searchResult[i].location && searchResult[i].location.lng || null,
|
|
address: searchResult[i].address || null,
|
|
category: searchResult[i].category || null,
|
|
tel: searchResult[i].tel || null,
|
|
adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
|
|
city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
|
|
district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
|
|
province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
|
|
});
|
|
}
|
|
param.success(data, {
|
|
searchResult: searchResult,
|
|
searchSimplify: searchSimplify
|
|
});
|
|
} else if (feature == 'suggest') {
|
|
const suggestResult = data.data;
|
|
const suggestSimplify = [];
|
|
for (let i = 0; i < suggestResult.length; i++) {
|
|
suggestSimplify.push({
|
|
adcode: suggestResult[i].adcode || null,
|
|
address: suggestResult[i].address || null,
|
|
category: suggestResult[i].category || null,
|
|
city: suggestResult[i].city || null,
|
|
district: suggestResult[i].district || null,
|
|
id: suggestResult[i].id || null,
|
|
latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
|
|
longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
|
|
province: suggestResult[i].province || null,
|
|
title: suggestResult[i].title || null,
|
|
type: suggestResult[i].type || null
|
|
});
|
|
}
|
|
param.success(data, {
|
|
suggestResult: suggestResult,
|
|
suggestSimplify: suggestSimplify
|
|
});
|
|
} else if (feature == 'reverseGeocoder') {
|
|
const reverseGeocoderResult = data.result;
|
|
const reverseGeocoderSimplify = {
|
|
address: reverseGeocoderResult.address || null,
|
|
latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
|
|
longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
|
|
adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
|
|
city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
|
|
district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
|
|
nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
|
|
province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
|
|
street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
|
|
street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
|
|
recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
|
|
rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
|
|
};
|
|
param.success(data, {
|
|
reverseGeocoderResult: reverseGeocoderResult,
|
|
reverseGeocoderSimplify: reverseGeocoderSimplify
|
|
});
|
|
} else if (feature == 'calculateDistance') {
|
|
const calculateDistanceResult = data.result.elements;
|
|
const distance = [];
|
|
for (let i = 0; i < calculateDistanceResult.length; i++){
|
|
distance.push(calculateDistanceResult[i].distance);
|
|
}
|
|
param.success(data, {
|
|
calculateDistanceResult: calculateDistanceResult,
|
|
distance: distance
|
|
});
|
|
} else {
|
|
param.success(data);
|
|
}
|
|
}
|
|
};
|
|
|
|
// QQMapWX类
|
|
class QQMapWX {
|
|
constructor(options) {
|
|
if (!options.key) {
|
|
throw Error('key值不能为空');
|
|
}
|
|
this.key = options.key;
|
|
}
|
|
|
|
// 逆地址解析
|
|
reverseGeocoder(options) {
|
|
const that = this;
|
|
options = options || {};
|
|
Utils.polyfillParam(options);
|
|
|
|
const requestParam = {
|
|
coord_type: options.coord_type || 5,
|
|
get_poi: options.get_poi || 0,
|
|
output: 'json',
|
|
key: that.key
|
|
};
|
|
if (options.poi_options) {
|
|
requestParam.poi_options = options.poi_options;
|
|
}
|
|
|
|
const locationsuccess = function (result) {
|
|
requestParam.location = result.latitude + ',' + result.longitude;
|
|
wx.request(Utils.buildWxRequestConfig(options, {
|
|
url: URL_GET_GEOCODER,
|
|
data: requestParam
|
|
}, 'reverseGeocoder'));
|
|
};
|
|
Utils.locationProcess(options, locationsuccess);
|
|
}
|
|
|
|
// POI周边检索
|
|
search(options) {
|
|
const that = this;
|
|
options = options || {};
|
|
Utils.polyfillParam(options);
|
|
|
|
if (!options.keyword) {
|
|
const errconf = Utils.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' keyword参数格式有误');
|
|
options.fail(errconf);
|
|
options.complete(errconf);
|
|
return;
|
|
}
|
|
|
|
const requestParam = {
|
|
keyword: options.keyword,
|
|
orderby: options.orderby || '_distance',
|
|
page_size: options.page_size || 10,
|
|
page_index: options.page_index || 1,
|
|
output: 'json',
|
|
key: that.key
|
|
};
|
|
|
|
if (options.address_format) {
|
|
requestParam.address_format = options.address_format;
|
|
}
|
|
|
|
if (options.filter) {
|
|
requestParam.filter = options.filter;
|
|
}
|
|
|
|
const distance = options.distance || "1000";
|
|
const auto_extend = options.auto_extend || 1;
|
|
|
|
const locationsuccess = function (result) {
|
|
requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
|
|
wx.request(Utils.buildWxRequestConfig(options, {
|
|
url: URL_SEARCH,
|
|
data: requestParam
|
|
}, 'search'));
|
|
};
|
|
|
|
Utils.locationProcess(options, locationsuccess);
|
|
}
|
|
|
|
// sug模糊检索
|
|
getSuggestion(options) {
|
|
const that = this;
|
|
options = options || {};
|
|
Utils.polyfillParam(options);
|
|
|
|
if (!options.keyword) {
|
|
const errconf = Utils.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' keyword参数格式有误');
|
|
options.fail(errconf);
|
|
options.complete(errconf);
|
|
return;
|
|
}
|
|
|
|
const requestParam = {
|
|
keyword: options.keyword,
|
|
region: options.region || '全国',
|
|
region_fix: options.region_fix || 0,
|
|
policy: options.policy || 0,
|
|
page_size: options.page_size || 10,
|
|
page_index: options.page_index || 1,
|
|
get_subpois: options.get_subpois || 0,
|
|
output: 'json',
|
|
key: that.key
|
|
};
|
|
|
|
if (options.address_format) {
|
|
requestParam.address_format = options.address_format;
|
|
}
|
|
|
|
if (options.filter) {
|
|
requestParam.filter = options.filter;
|
|
}
|
|
|
|
if (options.location) {
|
|
const locationsuccess = function (result) {
|
|
requestParam.location = result.latitude + ',' + result.longitude;
|
|
wx.request(Utils.buildWxRequestConfig(options, {
|
|
url: URL_SUGGESTION,
|
|
data: requestParam
|
|
}, "suggest"));
|
|
};
|
|
Utils.locationProcess(options, locationsuccess);
|
|
} else {
|
|
wx.request(Utils.buildWxRequestConfig(options, {
|
|
url: URL_SUGGESTION,
|
|
data: requestParam
|
|
}, "suggest"));
|
|
}
|
|
}
|
|
|
|
// 距离计算
|
|
calculateDistance(options) {
|
|
const that = this;
|
|
options = options || {};
|
|
Utils.polyfillParam(options);
|
|
|
|
if (!options.to) {
|
|
const errconf = Utils.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' to参数格式有误');
|
|
options.fail(errconf);
|
|
options.complete(errconf);
|
|
return;
|
|
}
|
|
|
|
const requestParam = {
|
|
mode: options.mode || 'walking',
|
|
to: options.to,
|
|
output: 'json',
|
|
key: that.key
|
|
};
|
|
|
|
if (options.from) {
|
|
options.location = options.from;
|
|
}
|
|
|
|
const locationsuccess = function (result) {
|
|
requestParam.from = result.latitude + ',' + result.longitude;
|
|
wx.request(Utils.buildWxRequestConfig(options, {
|
|
url: URL_DISTANCE,
|
|
data: requestParam
|
|
}, 'calculateDistance'));
|
|
};
|
|
|
|
Utils.locationProcess(options, locationsuccess);
|
|
}
|
|
}
|
|
|
|
return QQMapWX;
|
|
})();
|
|
|
|
// 全局QQMap实例
|
|
let qqmapInstance = null;
|
|
|
|
// 初始化腾讯地图SDK
|
|
function initQQMap() {
|
|
if (!qqmapInstance) {
|
|
try {
|
|
qqmapInstance = new QQMapWX({
|
|
key: QQMAP_KEY
|
|
});
|
|
console.log('腾讯地图SDK初始化成功');
|
|
} catch (err) {
|
|
console.error('初始化腾讯地图SDK失败:', err);
|
|
}
|
|
}
|
|
return qqmapInstance;
|
|
}
|
|
|
|
// 获取腾讯地图SDK实例
|
|
function getQQMapInstance() {
|
|
return qqmapInstance || initQQMap();
|
|
}
|
|
|
|
// 获取用户位置(使用微信的接口获取位置)
|
|
function getUserLocation() {
|
|
return new Promise((resolve, reject) => {
|
|
wx.getLocation({
|
|
type: 'gcj02',
|
|
success: (res) => {
|
|
resolve({
|
|
longitude: res.longitude,
|
|
latitude: res.latitude
|
|
});
|
|
},
|
|
fail: (error) => {
|
|
console.error('获取位置失败:', error);
|
|
reject(error);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 逆地理编码 - 根据经纬度获取地址信息
|
|
function getRegeo(longitude, latitude) {
|
|
return new Promise((resolve, reject) => {
|
|
const qqmap = getQQMapInstance();
|
|
if (!qqmap) {
|
|
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
|
return;
|
|
}
|
|
|
|
qqmap.reverseGeocoder({
|
|
location: {
|
|
latitude,
|
|
longitude
|
|
},
|
|
success: (data, result) => {
|
|
// 官方SDK返回的数据结构:data是原始数据,result是简化数据
|
|
const reverseGeocoderSimplify = result.reverseGeocoderSimplify;
|
|
resolve({
|
|
success: true,
|
|
data: {
|
|
formatted_address: reverseGeocoderSimplify.address,
|
|
addressComponent: {
|
|
city: reverseGeocoderSimplify.city,
|
|
district: reverseGeocoderSimplify.district,
|
|
province: reverseGeocoderSimplify.province,
|
|
street: reverseGeocoderSimplify.street,
|
|
street_number: reverseGeocoderSimplify.street_number
|
|
}
|
|
}
|
|
});
|
|
},
|
|
fail: (error) => {
|
|
console.error('逆地理编码失败:', error);
|
|
reject({ success: false, message: error.message || '逆地理编码失败' });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 搜索周边POI
|
|
function getPoiAround(longitude, latitude, keyword = '', radius = 1000) {
|
|
return new Promise((resolve, reject) => {
|
|
const qqmap = getQQMapInstance();
|
|
if (!qqmap) {
|
|
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
|
return;
|
|
}
|
|
|
|
qqmap.search({
|
|
keyword: keyword,
|
|
location: {
|
|
latitude,
|
|
longitude
|
|
},
|
|
distance: radius,
|
|
success: (data, result) => {
|
|
const searchSimplify = result.searchSimplify;
|
|
resolve({
|
|
success: true,
|
|
data: searchSimplify
|
|
});
|
|
},
|
|
fail: (error) => {
|
|
console.error('搜索POI失败:', error);
|
|
reject({ success: false, message: error.message || '搜索POI失败' });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 计算距离(异步)
|
|
function calculateDistance(from, to) {
|
|
return new Promise((resolve, reject) => {
|
|
const qqmap = getQQMapInstance();
|
|
if (!qqmap) {
|
|
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
|
return;
|
|
}
|
|
|
|
qqmap.calculateDistance({
|
|
from: from,
|
|
to: to,
|
|
mode: 'walking',
|
|
success: (data, result) => {
|
|
const distance = result.distance;
|
|
resolve({
|
|
success: true,
|
|
data: distance
|
|
});
|
|
},
|
|
fail: (error) => {
|
|
console.error('计算距离失败:', error);
|
|
reject({ success: false, message: error.message || '计算距离失败' });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 计算距离(同步,使用球面距离公式)
|
|
function calculateDistanceSync(lat1, lng1, lat2, lng2) {
|
|
const R = 6371000; // 地球半径(米)
|
|
const dLat = (lat2 - lat1) * Math.PI / 180;
|
|
const dLng = (lng2 - lng1) * Math.PI / 180;
|
|
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
|
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
|
|
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
return Math.round(R * c); // 返回距离,单位为米
|
|
}
|
|
|
|
// 关键词提示
|
|
function getSuggestion(keyword, region = '全国') {
|
|
return new Promise((resolve, reject) => {
|
|
const qqmap = getQQMapInstance();
|
|
if (!qqmap) {
|
|
reject({ success: false, message: '腾讯地图SDK未初始化' });
|
|
return;
|
|
}
|
|
|
|
qqmap.getSuggestion({
|
|
keyword: keyword,
|
|
region: region,
|
|
success: (data, result) => {
|
|
const suggestSimplify = result.suggestSimplify;
|
|
resolve({
|
|
success: true,
|
|
data: suggestSimplify
|
|
});
|
|
},
|
|
fail: (error) => {
|
|
console.error('关键词提示失败:', error);
|
|
reject({ success: false, message: error.message || '关键词提示失败' });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 导出函数
|
|
export {
|
|
getUserLocation,
|
|
getRegeo,
|
|
getPoiAround,
|
|
calculateDistance,
|
|
calculateDistanceSync,
|
|
getSuggestion,
|
|
initQQMap,
|
|
getQQMapInstance
|
|
};
|
|
|
|
// 测试距离计算函数(开发调试用)
|
|
export function testDistanceCalculation() {
|
|
// 测试用例:北京天安门到故宫的距离(约1.5公里)
|
|
const tiananmen = { lat: 39.908823, lng: 116.397470 };
|
|
const gugong = { lat: 39.916527, lng: 116.397128 };
|
|
|
|
const distance = calculateDistanceSync(tiananmen.lat, tiananmen.lng, gugong.lat, gugong.lng);
|
|
console.log('天安门到故宫的距离:', distance, '米');
|
|
console.log('转换为公里:', (distance / 1000).toFixed(2), '公里');
|
|
|
|
return distance;
|
|
}
|