index.obj.js 13 KB

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