手机指北针 + Python绘制徒步路线图

Posted Rolei_zl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手机指北针 + Python绘制徒步路线图相关的知识,希望对你有一定的参考价值。

  1. 问题:户外活动,深林密林没有信号,无GRPS,手机地图无法使用,无法了解行程和行动路线。
  2. 需求:绘制户外行动路线,标记关键行程点,留下行走路线图。
  3. 分析
    1)行动路线包括行动方向、行程长度
    2)指南针(手持/手机)可以指示和标记行动方向
    3)行程长度可以由 行动速度 * 行动时间获得
    4)行程关键点标识可以通过外部输入进行标记
    5)绘制简单行动路线(手工/程序),示意图,不计精确度
  4. 设计 -- 最简版
    1)使用手机指南针功能
    2)记录行走路线方向:沿路线行进方向
    3)标记关键行程点方向:线路拐点 + 行程关键标识
    4)Python解析关键行程点,绘制行路路线示意图
  5. 实现
    1)界面绘制
            
    2)手机指南针功能调用
    3)记录行进方向
    // 创建实例时注册方向传感器
    Button_Start.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
    
        /* 定义手机传感器 */
        // 获取传感器管理器
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        // 地磁传感器,单位是uT(微特斯拉),测量设备周围三个物理轴(x,y,z)的磁场
        masensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
        // 加速度传感器,单位是m/s2,测量应用于设备X、Y、Z轴上的加速度
        accesensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    
        /* 注册传感器 */
        // 注册地磁传感器
        sensorManager.registerListener(sensorEventListener, masensor,         SensorManager.SENSOR_DELAY_GAME);
        // 注册加速度传感器
        sensorManager.registerListener(sensorEventListener, accesensor,SensorManager.SENSOR_DELAY_GAME);
        }
    });
    
    
    // 响应传感器监听
    SensorEventListener sensorEventListener =  new SensorEventListener() {
        float[] acceleValues = new float[3];   //加速度传感器值
        float[] magneticValues = new float[3]; //地磁传感器值 
    
        @Override
        public void onSensorChanged(SensorEvent event) {
            // 获得传感器值
            if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){
                acceleValues = event.values.clone();
            }else if(event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD){
                magneticValues = event.values.clone();
            }
            float[] values = new float[3];
            float[] R = new float[9];
            SensorManager.getRotationMatrix(R, null, acceleValues, magneticValues);
            SensorManager.getOrientation(R, values);
            // 方向角,以北为正方向
            degrees = -(float)Math.toDegrees(values[0]); 
    };
    
    // 记录节点方向
    if(diff >= timeinterval) {  // 达到时间间隔
        // 记录当前时间(日期+时间)和方向角,最新记录最前记录
        String st = df.format(curDate.getTime()) + ":   " + degrees + "\\n" + etmout.getText().toString();
        etmout.setText(st);  // 显示在文本框中
        curDate = new Date(System.currentTimeMillis());
        ct = curDate.getTime();
        // 显示时间间隔
        tvtest.setText(String.valueOf(et) + " --- " + String.valueOf(ct));
    }
    
    4)记录关键行程点
          手机不识别这是哪里?
          好吧,停下自动记录,手工输入吧~~
    5)保存行程记录
          保存本机还要权限注册和申请,懒,先不做。
          好吧,手工拷贝并保存到本机。
    6)APP生成/发布
    // build.gradle
    // defaultConfig 增加以下节点,定义生成的APP应用名
    android.applicationVariants.all {
        variant ->
        variant.outputs.all {
            outputFileName = "CompassPath.apk"   
            }
        }
     5)调用行程记录使用Python绘图
    import math                           # 数据处理,主要是小数位数
    import matplotlib.pyplot as plt       # 画图
    
    # 对方向角文件(手机获取并手动何存)进行解析
    # 原文件格式:日期、时间、里程方向角、里程点名字
    # 记录分解为:日期、时间(小时、分、秒)、里程点方向X轴坐标、里程点方向Y轴坐标、里程点名字
    def get_direction(filename):
        pdate = []
        ptime = []
        pdirect = []
        pname = []
        pall = []  # date, hour, min, sec, X_direct-cos, Y_direct-sin, name
    
        # 读入文件
        with open("./"+filename,"r", encoding='utf-8') as fp:
            frls = fp.readlines()
            
            for fl in frls:
                pt = []
    
                ## replace all double blank            
                while fl.count("  ")>0:
                    fl = fl.replace("  "," ")
                pf = fl.split(" ",2)
    
                ## NULL or there is no dirction data
                if fl.strip() == "" or len(pf) < 3:
                    continue
                else:
                ## get date               
                    pdate.append(pf[0])
                    pt.append(pf[0])
                ## get time and split into hour, minute, second                
                    ptime.append(pf[1][0:-1].split(":"))
                    ptpf = pf[1][0:-1].split(":")
                    pt.append(int(ptpf[0]))
                    pt.append(int(ptpf[1]))
                    pt.append(int(ptpf[2]))
                ## get direction and address point description
                    pdir = pf[2].replace("\\n","").strip()
                    pp = 0
                    pn = ""
                    for i in pdir:
                        if i.isnumeric() or i == "." or i == "-":
                            continue
                        else:
                            pp = pdir.index(i)
                            break
    
                    if pp == 0:
                        pd = float(pdir) + 90
                        pname.append("?")
                        pn = "?"
                    else:
                        pd = float(pdir[0:pp]) + 90
                        pname.append(pdir[pp:len(pdir)].strip())
                        pn = pdir[pp:len(pdir)].strip()
                    
                ## 方向角转成 X,Y轴坐标     
                    pt.append(round(math.cos(math.pi*pd/180),2))
                    pt.append(round(math.sin(math.pi*pd/180),2))
    
                    pt.append(pn)
                    pall.append(pt)
    
        pall.reverse()
    
        return pall
    
    def drawpath(points):
        px = []
        py = []
        pname = []
    
        ppx = 0  # perious node x value
        pcx = 0  # current node x value
        ppy = 0  # perious node y value
        pcy = 0  # current node y value
        ph = 0   # hour
        pm = 0   # minutes
        ps = 0   # seconds
    
        totaltime = 0 
        for i in range(0,len(points)):        
            pi = points[i]
    
            ph = pi[1]
            pm = pi[2]
            ps = pi[3]
    
            ptime = 0   # time interval
            
            ## 累计路程 X, Y 
            if i > 0:
                pcx = pi[4] + ppx 
                pcy = pi[5] + ppy
                ptime = ((ph-points[i-1][1])*60*60 + (pm-points[i-1][2])*60 + (ps-points[i-1][3]))/60 # double length per 30 seconds
            else:
                pcx = pi[4]
                pcy = pi[5]
                ptime = 1.0
            ppx = pcx
            ppy = pcy
    
            totaltime = round(totaltime+ptime,1)     
            px.append(pcx)
            py.append(pcy)
            pn = ""
            if i == 0:
                pn = "Start: " + pi[6]
            elif i == len(points)-1:
                pn = "End: " + pi[6]
            else:
                pn = pi[6]
            pname.append(pn + ", " + str(math.ceil(ptime)) + "'" + ", " + str(totaltime) + "'")
    
        ## draw line with points address
        waypoints = [px, py]
        plt.plot(waypoints[0], waypoints[1], '^-b', MarkerEdgeColor='g',MarkerFaceColor='g')
        ## lable name
        for i in range(0,len(pname)):
            plt.text(px[i]+0.05,py[i]+0.1,pname[i],ha='center', va='center', color="blue",clip_on=True, fontsize=8)
        plt.grid(False)
        ## 坐标轴
        plt.tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
        plt.tick_params(axis='y', which='both', left=False, right=False, labelleft=False)
        ## 解决乱码
        plt.rcParams['font.sans-serif'] = ['SimHei']  # aovid wide code
        ## X,Y轴坐标起点 和 终点设置
        plt.xlim(math.floor(min(px)) if min(px)>0 else math.floor(min(px)),math.ceil(max(px)) if max(px)>0 else math.ceil(max(px)))
        plt.ylim(math.floor(min(py)) if min(py)>0 else math.floor(min(py)),math.ceil(max(py)) if max(py)>0 else math.ceil(max(py)))
        plt.title(label='地点名,阶段耗时,总耗时(分钟)',loc='left',fontsize=8)
        plt.show()    
    6)显示
    大概就是这个样子吧,每隔5秒记录一次方向角。对吗?
    实际操作时在关键里程点开始记录方向角,记录后停止,标记里程碑点名字,到下个关键里程点再次记录。
    好处?不用手机实时的记录那么多方向点,而且还要标记名字。
    似乎这个Time Interval有点鸡肋
                   
  6. 功能增强
    1)待完成功能
         * 根据行走时间,动态绘制行进路线中关键行程点的长短
         * 其他。。。边用边加
    2)文件保存
         需要调用和申请手机存储权限,暂不实现
    3)手机端直接绘制行程路线图
         懒,暂不实现
    4)行程高度获取
         等手机有了压力感应器再说,暂不实现 

以上是关于手机指北针 + Python绘制徒步路线图的主要内容,如果未能解决你的问题,请参考以下文章

数据可视化应用绘制空间地图(附R语言代码)

用Python绘制专业的K线图含源代码

用Python绘制专业的K线图含源代码

Python Matlab绘制曲线图

Python Matlab绘制曲线图

Python中matplotlib绘制折线图方法总结