index.obj.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // 引入uni-map-common公共模块
  2. const UniMap = require('uni-map-common');
  3. const configCenter = require("uni-config-center");
  4. // 读取配置中心地图配置
  5. var UniMapConfig = configCenter({ pluginId: 'uni-map' }).requireFile('config.js');
  6. // 本地地图配置
  7. var LocalMapConfig = {
  8. "default": "", // 默认使用的平台
  9. "key": {
  10. "qqmap": "", // 腾讯地图key
  11. "amap": "", // 高德地图key
  12. }
  13. }
  14. const db = uniCloud.database();
  15. const _ = db.command;
  16. const $ = _.aggregate;
  17. const opendbPoiDB = db.collection("opendb-poi");
  18. class MyError extends Error {
  19. constructor(errMsg, errCode = -1) {
  20. super(errMsg);
  21. this.err = {
  22. errCode,
  23. errMsg
  24. }
  25. }
  26. }
  27. module.exports = {
  28. _before: function() {
  29. // 如果配置中心不存在地图配置,则使用本地地图配置
  30. if (!UniMapConfig) {
  31. UniMapConfig = LocalMapConfig;
  32. }
  33. let defaultProvider = UniMapConfig.default || "qqmap";
  34. let params = this.getParams();
  35. let {
  36. provider = defaultProvider,
  37. needOriginalResult = false
  38. } = params[0] || {};
  39. const key = UniMapConfig.key[provider] || LocalMapConfig.key[provider];
  40. if (!key) {
  41. throw { errCode: -1, errMsg: `请在uni-config-center/uni-map/config.js中或LocalMapConfig中配置地图供应商${provider}对应的key` };
  42. }
  43. // 初始化实例
  44. let uniMap = new UniMap({
  45. provider: provider, // 指定使用哪家地图供应商
  46. key: key,
  47. needOriginalResult
  48. });
  49. this.uniMap = uniMap;
  50. // // 在这里可以做一些统一的前置处理,比如权限校验、参数校验等
  51. // let {
  52. // payload, // payload参数为前端传递的参数,可以在前端调用uni.chooseLocation时传递
  53. // } = this.getParams()[0] || {};
  54. // if (!payload) {
  55. // throw new MyError("payload参数不能为空", -1);
  56. // }
  57. // // 如果业务在uniCloud上,则直接在这里写判断逻辑即可
  58. // if (true) {
  59. // throw new MyError("权限不足", -1);
  60. // }
  61. // // 如果业务不在uniCloud上,可通过 uniCloud.request 调用自己的服务进行校验
  62. // const requestRes = await uniCloud.request({
  63. // method: 'POST',
  64. // url: '你自己的接口地址',
  65. // data: payload,
  66. // });
  67. // // 约定errCode不为0代表校验失败,errMsg为失败原因
  68. // if (requestRes.data.errCode !== 0) {
  69. // throw new MyError(requestRes.data.errMsg, requestRes.data.errCode);
  70. // }
  71. },
  72. _after: function(err, res) {
  73. if (err) {
  74. if (err.err) {
  75. return err.err;
  76. }
  77. if (err.errCode) {
  78. return err;
  79. }
  80. throw err; // 如果方法抛出错误,也直接抛出不处理
  81. }
  82. console.log("result", res.result);
  83. return res;
  84. },
  85. // 函数chooseLocation是给uni.chooseLocation使用,请勿修改chooseLocation函数的代码
  86. async chooseLocation(parame = {}) {
  87. let res = {};
  88. let {
  89. action,
  90. data,
  91. needOriginalResult
  92. } = parame;
  93. // 获取uniMap实例
  94. const uniMap = this.uniMap;
  95. // 调用API
  96. let result = await uniMap[action](data);
  97. res.result = needOriginalResult ? result.originalResult : result;
  98. // 模拟错误
  99. // res.errCode = 121;
  100. // res.errMsg = '此key每日调用量已达到上限'
  101. return res;
  102. },
  103. // 经纬度坐标转地址
  104. async location2address(data = {}) {
  105. let res = {};
  106. // 获取uniMap实例
  107. const uniMap = this.uniMap;
  108. // 调用API
  109. let result = await uniMap.location2address(data);
  110. res.result = result;
  111. return res;
  112. },
  113. // 地址转经纬度坐标
  114. async address2location(data = {}) {
  115. let res = {};
  116. // 获取uniMap实例
  117. const uniMap = this.uniMap;
  118. // 调用API
  119. let result = await uniMap.address2location(data);
  120. res.result = result;
  121. return res;
  122. },
  123. // 坐标系转换
  124. async translate(data = {}) {
  125. let res = {};
  126. // 获取uniMap实例
  127. const uniMap = this.uniMap;
  128. // 调用API
  129. let result = await uniMap.translate(data);
  130. res.result = result;
  131. return res;
  132. },
  133. // ip定位
  134. async ip2location(data = {}) {
  135. let res = {};
  136. // 获取uniMap实例
  137. const uniMap = this.uniMap;
  138. // 调用API
  139. let result = await uniMap.ip2location(data);
  140. res.result = result;
  141. return res;
  142. },
  143. // 输入提示
  144. async inputtips(data = {}) {
  145. let res = {};
  146. // 获取uniMap实例
  147. const uniMap = this.uniMap;
  148. // 调用API
  149. let result = await uniMap.inputtips(data);
  150. res.result = result;
  151. return res;
  152. },
  153. // 搜索
  154. async search(data = {}) {
  155. let res = {};
  156. // 获取uniMap实例
  157. const uniMap = this.uniMap;
  158. // 调用API
  159. let result = await uniMap.search(data);
  160. res.result = result;
  161. return res;
  162. },
  163. // 行政区划
  164. async districtSearch(data = {}) {
  165. let res = {};
  166. // 获取uniMap实例
  167. const uniMap = this.uniMap;
  168. // 调用API
  169. let result = await uniMap.districtSearch(data);
  170. res.result = result;
  171. return res;
  172. },
  173. // 路径规划
  174. async route(data = {}) {
  175. let res = {};
  176. // 获取uniMap实例
  177. const uniMap = this.uniMap;
  178. // 调用API
  179. let result = await uniMap.route(data);
  180. res.result = result;
  181. return res;
  182. },
  183. // 演示用 - 清空所有的测试POI
  184. async clearPoi(data = {}) {
  185. let res = { errCode: 0 };
  186. const db = uniCloud.database();
  187. await db.collection("opendb-poi").where({
  188. is_random: true
  189. }).remove();
  190. return res;
  191. },
  192. // 演示用 - 初始化静态001场景演示数据
  193. async initStatic001(data = {}) {
  194. let res = { errCode: 0 };
  195. const category = "static-001";
  196. // 先删除
  197. await opendbPoiDB.where({
  198. category: category
  199. }).remove();
  200. // 后添加随机数据
  201. // 以天安门为中心
  202. let tiananmen = {
  203. longitude: 116.39747,
  204. latitude: 39.908823,
  205. };
  206. let time = Date.now();
  207. // 随机生成6个门店地址
  208. let list = [];
  209. for (let i = 1; i <= 6; i++) {
  210. let randomCoordinate = getRandomCoordinateWithinRadius(tiananmen.longitude, tiananmen.latitude, 10); // 随机生成在天安门方圆X KM内的坐标
  211. list.push({
  212. category: category, // 场景值,用于区分这些POI所属哪张地图
  213. type: "门店",
  214. title: `随机门店-${i}`,
  215. location: new db.Geo.Point(randomCoordinate.longitude, randomCoordinate.latitude),
  216. create_date: time,
  217. visible: true,
  218. is_random: true, // 表示此为随机生成的点,方便删除
  219. level: i
  220. });
  221. }
  222. // 随机生成1个总部地址
  223. let randomCoordinate = getRandomCoordinateWithinRadius(tiananmen.longitude, tiananmen.latitude, 1); // 随机生成在天安门方圆X KM内的坐标
  224. list.push({
  225. category: category, // 场景值,用于区分这些POI所属哪张地图
  226. type: "总部",
  227. title: `随机总部`,
  228. location: new db.Geo.Point(randomCoordinate.longitude, randomCoordinate.latitude),
  229. create_date: time,
  230. visible: true,
  231. is_random: true, // 表示此为随机生成的点,方便删除
  232. level: 7
  233. });
  234. // 添加到数据库
  235. await opendbPoiDB.add(list);
  236. return res;
  237. },
  238. // 演示用 - 初始化动态001场景演示数据(模拟送外卖场景)
  239. async initDynamics001(data = {}) {
  240. let res = { errCode: 0 };
  241. const category = "dynamics-001";
  242. // 先删除
  243. await opendbPoiDB.where({
  244. category: category
  245. }).remove();
  246. // 后添加随机数据
  247. // 以天安门为中心
  248. let tiananmen = {
  249. longitude: 116.39747,
  250. latitude: 39.908823,
  251. };
  252. let time = Date.now();
  253. // 随机生成配送员坐标
  254. let randomCoordinate1 = getRandomCoordinateWithinRadius(tiananmen.longitude, tiananmen.latitude, 2); // 随机生成在天安门方圆X KM内的坐标
  255. let data1 = {
  256. category: category, // 场景值,用于区分这些POI所属哪张地图
  257. type: "配送员",
  258. title: "配送员",
  259. location: new db.Geo.Point(randomCoordinate1.longitude, randomCoordinate1.latitude),
  260. create_date: time,
  261. visible: true,
  262. is_random: true, // 表示此为随机生成的点,方便删除
  263. }
  264. // 随机生成目的地坐标
  265. let randomCoordinate2 = getRandomCoordinateWithinRadius(tiananmen.longitude, tiananmen.latitude, 2); // 随机生成在天安门方圆X KM内的坐标
  266. let data2 = {
  267. category: category, // 场景值,用于区分这些POI所属哪张地图
  268. type: "目的地",
  269. title: "配送目的地",
  270. location: new db.Geo.Point(randomCoordinate2.longitude, randomCoordinate2.latitude),
  271. create_date: time,
  272. visible: true,
  273. is_random: true, // 表示此为随机生成的点,方便删除
  274. }
  275. let list = [data1, data2];
  276. // 添加到数据库
  277. await opendbPoiDB.add(list);
  278. // 获取配送路线
  279. // 获取uniMap实例
  280. const uniMap = this.uniMap;
  281. // 调用电瓶车路径规划API
  282. let result = await uniMap.route({
  283. mode: "ebicycling",
  284. from: `${randomCoordinate1.latitude},${randomCoordinate1.longitude}`,
  285. to: `${randomCoordinate2.latitude},${randomCoordinate2.longitude}`,
  286. alternative_route: 1
  287. });
  288. let route = result.result.routes[0];
  289. let { steps = [] } = route;
  290. let points = [];
  291. steps.map((step) => {
  292. let {
  293. polyline = ""
  294. } = step;
  295. let arr = polyline.split(";");
  296. arr.map((item) => {
  297. let arr2 = item.split(",");
  298. points.push({
  299. latitude: arr2[0],
  300. longitude: arr2[1],
  301. });
  302. });
  303. });
  304. let polyline = {
  305. points,
  306. color: "#19b411",
  307. width: 6,
  308. dottedLine: false,
  309. arrowLine: true,
  310. borderWidth: 1,
  311. borderColor: "#000000",
  312. };
  313. res.polyline = [polyline];
  314. return res;
  315. },
  316. // 演示用 - 获取配送员配送路径
  317. async getPolyline(data = {}) {
  318. let res = { errCode: 0 };
  319. const category = "dynamics-001";
  320. let getRes1 = await opendbPoiDB.where({
  321. category: category,
  322. type: "配送员",
  323. visible: true
  324. }).get();
  325. let poi1 = getRes1.data[0];
  326. let getRes2 = await opendbPoiDB.where({
  327. category: category,
  328. type: "目的地",
  329. visible: true
  330. }).get();
  331. let poi2 = getRes2.data[0];
  332. if (!poi2) {
  333. return {
  334. errCode: 0,
  335. end: true
  336. }
  337. }
  338. let coordinate1 = {
  339. longitude: poi1.location.coordinates[0],
  340. latitude: poi1.location.coordinates[1]
  341. };
  342. let coordinate2 = {
  343. longitude: poi2.location.coordinates[0],
  344. latitude: poi2.location.coordinates[1]
  345. };
  346. // 获取uniMap实例
  347. const uniMap = this.uniMap;
  348. // 调用电瓶车路径规划API
  349. let result = await uniMap.route({
  350. mode: "ebicycling",
  351. from: `${coordinate1.latitude},${coordinate1.longitude}`,
  352. to: `${coordinate2.latitude},${coordinate2.longitude}`,
  353. alternative_route: 1
  354. });
  355. let route = result.result.routes[0];
  356. //console.log('route: ', route)
  357. let { steps = [], distance, duration } = route;
  358. let points = [];
  359. let dir_desc;
  360. steps.map((step) => {
  361. let {
  362. polyline = ""
  363. } = step;
  364. if (!dir_desc) dir_desc = step.dir_desc;
  365. if (polyline) {
  366. let arr = polyline.split(";");
  367. arr.map((item) => {
  368. let arr2 = item.split(",");
  369. if (!isNaN(arr2[0]) && !isNaN(arr2[1])) {
  370. points.push({
  371. latitude: Number(arr2[0]),
  372. longitude: Number(arr2[1]),
  373. });
  374. }
  375. });
  376. }
  377. });
  378. let polyline = {
  379. points,
  380. color: "#19b411",
  381. width: 6,
  382. dottedLine: false,
  383. arrowLine: true,
  384. borderWidth: 1,
  385. borderColor: "#000000",
  386. };
  387. res.polyline = [polyline];
  388. if (distance <= 30 || duration <= 0) {
  389. await opendbPoiDB.doc(poi1._id).update({
  390. title: `配送员已到达目的地`,
  391. location: new db.Geo.Point(Number(coordinate2.longitude), Number(coordinate2.latitude)),
  392. rotate: 0
  393. });
  394. // 隐藏目的地
  395. await opendbPoiDB.doc(poi2._id).update({
  396. visible: false,
  397. });
  398. return {
  399. errCode: 0,
  400. end: true
  401. }
  402. } else {
  403. // 从最近2个点计算出当前行驶方向
  404. let rotate = 0;
  405. if (points && points.length >= 2) {
  406. rotate = calculateDirectionAngle(points[0], points[1]);
  407. }
  408. await opendbPoiDB.doc(poi1._id).update({
  409. title: `配送员正在配送\r\n还有 ${distance} 米\r\n预计 ${duration} 分钟送达`,
  410. rotate: rotate, // 设置角度,0°的图片方向应朝左(西) 故90° 朝上(北) 180° 朝右(东) 270° 朝下(南)
  411. });
  412. }
  413. return res;
  414. },
  415. // 演示用 - 模拟上报配送员坐标
  416. async updateMyLocation(data = {}) {
  417. let res = {};
  418. const category = "dynamics-001";
  419. let {
  420. longitude,
  421. latitude
  422. } = data;
  423. let getRes1 = await opendbPoiDB.where({
  424. category: category,
  425. type: "配送员",
  426. visible: true
  427. }).get();
  428. let poi1 = getRes1.data[0];
  429. await opendbPoiDB.doc(poi1._id).update({
  430. location: new db.Geo.Point(Number(longitude), Number(latitude))
  431. });
  432. return res;
  433. },
  434. // 演示用 - xxxx
  435. async test(data = {}) {
  436. let res = {};
  437. // 获取uniMap实例
  438. const uniMap = this.uniMap;
  439. // 调用API
  440. let result = await uniMap.location2address({
  441. });
  442. res.result = result;
  443. return res;
  444. }
  445. }
  446. /**
  447. * 生成在指定经纬度圆内的随机坐标
  448. const latitude = 39.908823; // 指定纬度
  449. const longitude = 116.39747; // 指定经度
  450. const radiusInKm = 10; // 指定圆的半径(单位:千米)
  451. const randomCoordinate = getRandomCoordinateWithinRadius(latitude, longitude, radiusInKm);
  452. console.log(randomCoordinate);
  453. */
  454. function getRandomCoordinateWithinRadius(longitude, latitude, radiusInKm) {
  455. // 地球半径(单位:千米)
  456. const earthRadius = 6371;
  457. // 将圆的半径转换为弧度
  458. const radiusInRad = radiusInKm / earthRadius;
  459. // 生成随机的方位角(弧度,0到2π)
  460. const randomAngleRad = Math.random() * 2 * Math.PI;
  461. // 生成随机的距离(弧度,0到圆的半径)
  462. const randomDistanceRad = Math.acos(Math.random() * (Math.cos(radiusInRad) - 1) + 1);
  463. // 使用球面三角学计算随机点的纬度和经度
  464. const randomLatitudeRad = latitude * (Math.PI / 180) + randomDistanceRad * Math.cos(randomAngleRad);
  465. const randomLongitudeRad = longitude * (Math.PI / 180) + randomDistanceRad * Math.sin(randomAngleRad) / Math.cos(latitude * (Math.PI / 180));
  466. // 转换为度,并保留6位小数
  467. const randomLatitude = parseFloat((randomLatitudeRad * (180 / Math.PI)).toFixed(6));
  468. const randomLongitude = parseFloat((randomLongitudeRad * (180 / Math.PI)).toFixed(6));
  469. return { latitude: randomLatitude, longitude: randomLongitude };
  470. }
  471. /**
  472. * 计算坐标B在坐标A的方向,0代表正西方 90 代表正北方
  473. const latitude = 39.908823; // 指定纬度
  474. const longitude = 116.39747; // 指定经度
  475. const radiusInKm = 10; // 指定圆的半径(单位:千米)
  476. const randomCoordinate = getRandomCoordinateWithinRadius(latitude, longitude, radiusInKm);
  477. console.log(randomCoordinate);
  478. */
  479. function calculateDirectionAngle(coordA, coordB) {
  480. const toRadians = (angle) => angle * (Math.PI / 180);
  481. const toDegrees = (angle) => angle * (180 / Math.PI);
  482. const lat1 = toRadians(coordA.latitude);
  483. const lon1 = toRadians(coordA.longitude);
  484. const lat2 = toRadians(coordB.latitude);
  485. const lon2 = toRadians(coordB.longitude);
  486. const dLon = lon2 - lon1;
  487. const y = Math.sin(dLon) * Math.cos(lat2);
  488. const x =
  489. Math.cos(lat1) * Math.sin(lat2) -
  490. Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
  491. const angleRadians = Math.atan2(y, x);
  492. let angleDegrees = toDegrees(angleRadians);
  493. angleDegrees = (angleDegrees + 360) % 360;
  494. angleDegrees = (angleDegrees > 180) ? angleDegrees - 180 : angleDegrees + 180;
  495. angleDegrees -= 90; // 以正西方为0°表示,因此需要-90
  496. return angleDegrees;
  497. }