制作自己的骑行轨迹

马上国庆节了,计划骑车回家,突然想到把所有的骑行线路汇总一下,无奈码表和APP不支持这样的操作,出于职业病,在此操作一下。

我用的是黑鸟码表,可以导出fit运动轨迹,但是fit还需要转gpx格式才能读取,否在二进制无法读取。想到在官网也可以看骑行记录,做为一名开发,我嗅到了可以从官网的个人中心去爬取骑行数据,比直接读取文件更为方便和快捷。

效果

链接地址 壮壮壮壮壮的骑行记录

获取轨迹数据

登陆

登陆黑鸟官网,右上角进入个人中心

查单条数据

F12打开开发人员选项后,随便点击一条骑行记录进入详情,查看网络选项卡,找到请求骑行数据的请求,做为GISer,对经纬度格外敏感,看到38,114就知道这是需要的数据了。

提取骑行数据

提取骑行数据,postman里测试,设置请求头的Cookie,成功请求到数据。各个网站验证可能是不一样的,有的用Cookie,有的用Token,有的用Authorization,酌情而定。

记住请求地址中的关键信息 56135240 ,推测此为记录ID,后面肯定会在列表中存在。

http://www.blackbirdsport.com/api/records/56135240/data

提取骑行列表

提取骑行列表,返回上一页,查看网络选项卡,找到请求骑行列表的数据,在记录里找到recordID,和前面查询具体数据的关键字一致,那么就可以准备爬下所有数据了。

postman测试地址,正常返回所有数据。http://www.blackbirdsport.com/api/records?lastRecordId=0&pageSize=25 末尾的 pageSize=25 改为 pageSize=100 一次爬取100条数据,小菜腿还没骑够100次

爬取

java爬取,数据输出到控制台,复制保存好,接下来前台会用到,至此,数据爬取已经完成。

  1. import cn.hutool.core.util.StrUtil;
  2. import cn.hutool.http.HttpRequest;
  3. import cn.hutool.json.JSONArray;
  4. import cn.hutool.json.JSONObject;
  5. import cn.hutool.json.JSONUtil;
  6. /**
  7. * 获取黑鸟的行车行车数据
  8. * @author lizhuang
  9. * @date 2021/9/9 14:00
  10. */
  11. public class GetBickRecord {
  12. public static void main(String[] args) {
  13. String detailUrl = "http://www.blackbirdsport.com/api/records/{}/data";
  14. String listUrl = "http://www.blackbirdsport.com/api/records?lastRecordId=0&pageSize=100";
  15. String cookie = "xxxxxx";//自己在网站上获取的cookie
  16. String strRes = HttpRequest.get(listUrl).header("Cookie",cookie).execute().body();
  17. JSONObject o = JSONUtil.parseObj(strRes);
  18. JSONArray list = o.getJSONArray("content");
  19. JSONArray jSONArray = new JSONArray();
  20. for (int i = 0; i < list.size(); i++) {
  21. JSONObject obj = list.getJSONObject(i);
  22. String temp = StrUtil.format(detailUrl, obj.getStr("recordId"));
  23. String strData = HttpRequest.get(temp).header("Cookie",cookie).execute().body();
  24. String[] track = JSONUtil.parseObj(strData).getJSONObject("content").getStr("track").split(";");
  25. JSONArray j = new JSONArray();
  26. StringBuilder sb = new StringBuilder();
  27. try {
  28. Thread.sleep(1000);
  29. }catch (Exception e){
  30. }
  31. for (int i1 = 0; i1 < track.length; i1++) {
  32. String[] s1 = track[i1].split(",");
  33. JSONArray json = new JSONArray();
  34. json.add(s1[0]);
  35. json.add(s1[1]);
  36. json.add(s1[2]);
  37. j.add(json);
  38. }
  39. jSONArray.add(j);
  40. }
  41. System.out.println(jSONArray.toString());
  42. }
  43. }

页面制作

2102年了,当然要三维了。

关于展示想法

  • 想做成骑行次数越多的线路颜色越亮,最好在加上动态效果,也就OD线可以实现了。

  • 矢量地图和影像地图切换,采用百度暗色系矢量地图和天地图影像,天地图加载速度时快时慢,其他互联网影像精度不够,国外大厂又被墙了,只能用天地图了。

  • 关于地形,加载了地形后,发现OD线不能贴地,像要贴地只能改为普通线,暂时把地形去掉了,后面想到好方法再往上加吧。

  • 地形+普通线贴地的效果图

代码

直接上代码吧,前台使用cesium,采用百度暗色系底图,线条采用OD线。

  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <!-- <title>骑行记录</title> -->
  8. <!-- 0 引入js文件:XbsjEarth.js和vue.min.js -->
  9. <script src="../lib/XbsjEarth/XbsjEarth.js"></script>
  10. <!-- <script src="http://earthsdk.com/v/last/XbsjEarth/XbsjEarth.js"></script> -->
  11. <script src="./scripts/vue.min.js"></script>
  12. <!-- <script src="http://earthsdk.com/v/last/Apps/Examples/scripts/vue.min.js"></script> -->
  13. <style>
  14. html,
  15. body {
  16. width: 100%;
  17. height: 100%;
  18. margin: 0px;
  19. padding: 0px;
  20. overflow: hidden;
  21. }
  22. </style>
  23. </head>
  24. <body>
  25. <div id="vueApp" style="width: 100%; height: 100%; background: grey; position: relative;">
  26. <earth-comp></earth-comp>
  27. </div>
  28. <script>
  29. function switchMap(){
  30. earth.sceneTree.root.children[1].enabled = !earth.sceneTree.root.children[1].enabled
  31. }
  32. function loadData(dataFunc) {
  33. var data = [[["38.07765", "114.53156"], ["38.07769", "114.53156"]]] //'此处为上面java代码System.out.println()输出的数据,三位数组'
  34. var timeDuration = 10.0;
  35. var moveBaseDuration = 4.0;
  36. var hStep = 300 / (data.length - 1);
  37. var busLines = [];
  38. data.map(function (busLine, idx) {
  39. var points = [];
  40. busLine.map(function (item, idx) {
  41. if (Cesium.Math.toRadians(item[1]) != 0) {
  42. // points.push([Cesium.Math.toRadians(item[1]), Cesium.Math.toRadians(item[0]),item[2]]) 此为添加z值的数据(x,y,z)
  43. points.push([Cesium.Math.toRadians(item[1]), Cesium.Math.toRadians(item[0])]) // 仅有x,y
  44. }
  45. })
  46. busLines.push({
  47. positions: points,
  48. color: [Math.random() * 0.5 + 0.5, Math.random() * 0.8 + 0.2, 0.0, 1.0],
  49. width: 2.0,
  50. startTime: timeDuration * Math.random(),
  51. duration: moveBaseDuration + 1.0 * Math.random()
  52. });
  53. });
  54. console.log('busLines', busLines)
  55. dataFunc(busLines, timeDuration);
  56. }
  57. // 1 创建Earth的vue组件
  58. var EarthComp = {
  59. template: `
  60. <div style="width: 100%; height: 100%">
  61. <div ref="earthContainer" style="width: 100%; height: 100%">
  62. </div>
  63. <div style=" position: absolute; z-index: 99; display: inline-block; left: 2%; top: 2%;">
  64. <button style=" width: 130px; height: 40px; color: #fff; border-radius: 5px; padding: 10px 25px; font-family: 'Lato', sans-serif; font-weight: 500; background: transparent; cursor: pointer; transition: all 0.3s ease; position: relative; display: inline-block; box-shadow: inset 2px 2px 2px 0px rgb(255 255 255 / 50%), 7px 7px 20px 0px rgb(0 0 0 / 10%), 4px 4px 5px 0px rgb(0 0 0 / 10%); outline: none;" onclick="switchMap()">切换地图</button>
  65. </div>
  66. </div>
  67. `,
  68. data() {
  69. return {
  70. _earth: undefined, // 注意:Earth和Cesium的相关变量放在vue中,必须使用下划线作为前缀!
  71. _bgImagery: undefined,
  72. fps: 0,
  73. elapsedTime: 0
  74. };
  75. },
  76. // 1.1 资源创建
  77. mounted() {
  78. // 1.1.1 创建地球
  79. var earth = new XE.Earth(this.$refs.earthContainer);
  80. // 1.1.2 添加默认地球影像
  81. earth.sceneTree.root = {
  82. "children": [
  83. {
  84. "czmObject": {
  85. "xbsjType": "Imagery",
  86. "xbsjImageryProvider": {
  87. "XbsjImageryProvider": {
  88. "url": "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}",
  89. }
  90. }
  91. }
  92. },
  93. {
  94. "czmObject": {
  95. "xbsjType": "Imagery",
  96. "xbsjImageryProvider": {
  97. "XbsjImageryProvider": {
  98. "url": "http://t6.tianditu.com/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=2cda5caa13d033e23a39407fc62accdb",
  99. "dstCoordType": "GCJ02"
  100. }
  101. }
  102. }
  103. },
  104. /** 中国14级地形
  105. {
  106. "czmObject": {
  107. "xbsjType": "Terrain",
  108. "xbsjTerrainProvider": {
  109. "type": "XbsjCesiumTerrainProvider",
  110. "XbsjCesiumTerrainProvider": {
  111. "url": "http://lab.earthsdk.com/terrain/577fd5b0ac1f11e99dbd8fd044883638",
  112. "requestVertexNormals": true,
  113. "requestWaterMask": true
  114. }
  115. }
  116. }
  117. }
  118. */
  119. ]
  120. }
  121. earth.sceneTree.root.children[1].enabled = false
  122. earth._viewer.camera.flyTo({
  123. destination: new Cesium.Cartesian3(-2102351.020236049, 4605886.135770324, 3909950.0555654215),
  124. orientation: {
  125. heading: 6.270938378736673,
  126. pitch: -0.8989671219533877,
  127. roll: 6.282706587562512
  128. },
  129. duration: 3
  130. });
  131. this._odlines = new XE.Obj.ODLines(earth);
  132. this._odlines.translucentPass = false;
  133. this._odlines.color = [1, 1, 1, 1];
  134. window.earth = earth
  135. window.odlines = this._odlines
  136. loadData((data, timeDuration) => {
  137. this._odlines.data = data;
  138. this._odlines.timeDuration = timeDuration;
  139. this._odlines.playing = true;
  140. });
  141. },
  142. // 1.2 资源销毁
  143. beforeDestroy() {
  144. // vue程序销毁时,需要清理相关资源
  145. this._fpsUnbind = this._fpsUnbind && this._fpsUnbind();
  146. this._elapsedTimeUnbind = this._elapsedTimeUnbind && this._elapsedTimeUnbind();
  147. this._earth = this._earth && this._earth.destroy();
  148. },
  149. }
  150. // 2 创建vue程序
  151. // XE.ready()用来加载Cesium.js等相关资源
  152. XE.ready().then(() => {
  153. var app = new Vue({
  154. el: '#vueApp',
  155. components: {
  156. EarthComp,
  157. },
  158. });
  159. });
  160. </script>
  161. </body>
  162. </html>

不足

OD线效果在手机浏览器里无法显示,后面想办法解决一下 地形 + 贴地、移动端适配的问题。

原文地址

洒家废物的博客

cesium制作自己的骑行轨迹的更多相关文章

  1. 使用python进行运动轨迹合并:多次骑行跑步轨迹叠加显示

    现有各种各样的运动app.运动手表手环以及gps码表等可以用于记录日常骑行或跑步等运动轨迹;但轨迹显示多数只限于显示一天的轨迹,经过搜索只发现一篇文章介绍跑步轨迹叠加方法(查看),根据教程尝试了下还因 ...

  2. 智能头盔 "Livall携全球首款智能骑行头盔亮相CES"

    LIVALL是全球首创集音乐.通讯.智能灯光为一体的智能骑行头盔的研发者,日前Livall携旗下智能骑行头盔BH 100和BH 60参展CES 2017,这也是目前世全球首款智能骑行头盔类产品,同时亮 ...

  3. 黑鸟码表BB10S骑行记录导入行者

    前言 开始骑车用行者app记录, 后来觉得每次都要开app很麻烦, 于是在骑友的推荐下入手了黑鸟BB10S, 使用了一段时间感觉还不错, 不过也遇到之前大家说的问题, 黑鸟不支持直接导出fit文件, ...

  4. 2876: [Noi2012]骑行川藏 - BZOJ

    Description 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因 ...

  5. bzoj 2876: [Noi2012]骑行川藏 拉格朗日数乘

    2876: [Noi2012]骑行川藏 Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 1033  Solved: ...

  6. 高等数学(拉格朗日乘子法):NOI 2012 骑行川藏

    [NOI2012] 骑行川藏 输入文件:bicycling.in   输出文件:bicycling.out   评测插件 时间限制:1 s   内存限制:128 MB NOI2012 Day1 Des ...

  7. bzoj2876 [Noi2012]骑行川藏

    Description 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因 ...

  8. bzoj2876 [NOI2012]骑行川藏(拉格朗日乘数法)

    题目描述 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨.川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的体力十分有限,因此在每天的骑行 ...

  9. BZOJ 2876 【NOI2012】 骑行川藏

    题目链接:骑行川藏 听说这道题需要一些高数知识 于是膜了一发dalao的题解……然后就没了…… 不要吐槽我的精度TAT……eps设太小了就TLE,大了就Wa……我二分的边界是对着数据卡的…… 下面贴代 ...

随机推荐

  1. Wannafly挑战赛10F-小H和遗迹【Trie,树状数组】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/72/F 题目大意 \(n\)个字符串,包括小写字母和\(\#\).其中\(\#\)可以替换为任意字符串.求有多 ...

  2. Unittest 框架之断言,你学会了吗??

    unittest断言 Python在 unittest.TestCase 类中提供了很多断言方法.断言方法检查你认为应该满足的条件是否确实满足.如果该条件确实满足,你对程序行为的假设就得到了确认,你就 ...

  3. State Space Model Content

    State Space Model 状态空间模型及其卡尔曼滤波技术 混合正态分布下的状态空间模型及其滤波

  4. 聊聊并发(一)——初始JUC

    一.volatile 1.介绍 JDK 5.0 提供了java.util.concurrent包,在此包中增加了并发编程中很常用的使用工具类,用于定义类似于线程的自定义子系统,包括线程池.异步IO和轻 ...

  5. 实验3:OpenFlow协议分析实践

    作业链接:实验3:OpenFlow协议分析实践 一.实验目的 能够运用 wireshark 对 OpenFlow 协议数据交互过程进行抓包: 能够借助包解析工具,分析与解释 OpenFlow协议的数据 ...

  6. farOs 介绍

    nGame nGame 一款文字游戏服务端框架;用于快速构建:自由探索.武侠.修真.模拟,回合制,剧本杀.动态语言小说.等服务器 如果你有期望实现的功能请加Q群 ngame计划 完善框架功能 farO ...

  7. 数据结构与算法——迪杰斯特拉(Dijkstra)算法

    tip:这个算法真的很难讲解,有些地方只能意会了,多思考多看几遍还是可以弄懂的. 应用场景-最短路径问题 战争时期,胜利乡有 7 个村庄 (A, B, C, D, E, F, G) ,现在有六个邮差, ...

  8. logstash输出到rabbitmq

    场景 将应用日志文件发送到rabbitmq. filebeat 不支持rabbitmq作为输出.因此,需要先将文件由filebeat发送到logstash ,再由logstash 输出到rabbitm ...

  9. Billu_b0x2内网渗透(多种提权方法)靶场-vulnhub

    个人博客阅读体验更佳 本次来试玩一下vulnhub上的Billu_b0x2,下载地址. 下载下来后是 .ova 格式,建议使用vitualbox进行搭建,vmware可能存在兼容性问题.靶场推荐使用N ...

  10. Golang通脉之基础入门

    为什么要学 Go 性能优越感:Go 极其地快,其性能与 Java 或 C++相似.在使用中,Go 一般比 Python 要快 30 倍: 序列化/去序列化.排序和聚合中表现优异: 开发者效率较高:多种 ...