// 地图工具函数 - 内联腾讯地图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; }