安卓端益动GPS数据导出轨迹信息


本文写于2024年2月5日
益动GPS应该是国内最早一批做定位轨迹以及记录运动的App,非常可惜,当时我记得是乐视注资了还是投资了,结果后面也是慢慢停止服务了,最开始是同步不了记录,微博还有人运营,到后面微博也人运营, 官网也打不开,app登录不上去了。
追溯到最早用这个app的时候还是2014年。

益动GPS历史记录

让我耿耿于怀的是,其中有一条我骑行了一整天共130多公里记录的,从7点不到出门,到快7点才到家,这条记录一直都在手机里存着, 伴随着我换了两次手机,这个app还在我手机里躺着。
当时奈何技术不够,一直想用什么方法导出来,年轻的菜鸟终于有折腾出来方法了。

思路:

  • 用安卓的adb工具,备份出app的所有数据。(发现MIUI直接在手机的备份中,选定应用,备份一下导出电脑解压就可以了。)
  • 分析备份出来的内容,发现有db结尾后缀的文件,熟悉开发的人都知道,这是Sqlite数据库的文件。
  • 解析数据,导出gpx文件格式的数据
  • 发现还不能用,还得进行转换,才能用到国内的地图源上 WGS->GCJ
  • 轨迹叠加地图导出视频 GPX animator / Telemetry Overlay / 佳明Virb

备份【益动GPS】并解压出文件

手机用的都是几年前的红米手机,导出数据的过程比较顺利,开始以为一定要用adb工具的backup命令才能备份出来,结果发现MIUI自带的备份,会直接把应用数据打包, 也没有加密,不知道新版的MIUI还行不行。
测试 基于MIUI 12.0.11 Redmi Note 9 Pro Android 10 / MIUI12.0.2 XiaoMi 6X Andoroid 9 / Miui XiaoMi 4C (4C不记得具体版本了..)

【设置】——【我的设备】——【备份与恢复】——【手机备份恢复】——【输入锁屏密码】——【选择第三方应用】——【开始备份】

MIUI备份1
MIUI备份1

然后从手机上拷贝对应路径 /存储根目录/MIUI/backup/AllBackup/[备份日期_时间]/益动GPS(com.edooon.gps).bak
到电脑上,直接用解压缩软件,解压出来。

分析备份出的应用数据

db文件夹中,包含一些.db后缀的文件,从大到小排序,不出以为,数据应该在最大的.db文件里。

随便用个Sqlite查看器打开,果不其然。并且找到第一条记录时2016年1月6日的。

匹配记录

数据库表:

表名 描述
android_metadata 安卓元数据
chat_friends 聊天?私信?
record_best 最佳记录
record_details 记录明细
record_points 记录点

以上最重要的就是 record_points 表,奇怪的是,app界面显示最早的数据是2014年,但是这个表里却没有。
果断按距离排序,找到了我那条心心念念的130多公里的数据了。

数据ID 开始时间 结束时间 卡路里 lat偏移 lon偏移 距离 平均速度 ...
700 1460327870 1460377716 4792 -0.003391 0.004833 135861.078125 11271.66015625 ...

对应记录ID为700的record_points数据也在。
格式为经纬度、高度、花费时间、速度和距离等字段。
接下来就是编程去导出数据,导出标准的gpx文件了。

导出数据-->GPX

鄙人用的C#熟练一些, 就直接找了现成的东西用了。
NuGet引用:

  • Drapper ORM框架,直接拿数据
  • Geo 处理GPX文件,支持读取和创建GPX格式文件

数据库工具:

  • PDManner 元数建模 国产的数据库建模平台 元数建模

编码过程就不写了,直接上代码了,目前完成了

  • 导出所有有记录点的轨迹,格式是yyyy-MM-dd_HH-mm-ss.gpx
  • 运行时加入gpx文件完整路径名称,转换成GCJ-02格式并输出GCJ-02_yyyy-MM-dd_HH-mm-ss.gpx

RecordDetails模型类

点击查看代码 RecordDetails类
public partial class RecordDetails
{

    /// <summary>
    /// ;
    /// </summary>
    public int _Id { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Start_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int End_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Calorie { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Lat_Offset { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Lon_Offset { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Distance { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Avg_Speed { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Max_Speed { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Min_Speed { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Min_Altitude { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Max_Altitude { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Status { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Server_Id { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Last_Pause_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Total_Pause_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Duration { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Sport_Type { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Recordpoints_Count { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Last_Km_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Is_From_Server { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Source { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string Device_Brand { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string Device_Type { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string Device_Sn { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string Brand_Link { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string Brand_Image { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Avg_Heart_Rate { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Max_Heart_Rate { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Min_Heart_Rate { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Avg_Slope { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Step_Count { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public string User_Id { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int State { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Locationprivacy { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Goal_Distance { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Goal_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Goal_Continue { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Gps_Type { get; set; }
}

RecordPoints模型类

点击查看代码 RecordPoints类
public partial class RecordPoints
{

    /// <summary>
    /// ;
    /// </summary>
    public int _Id { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Recorddetail_Id { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Latitude { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Longitude { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public double Altitude { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Speed { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Accuracy { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public decimal Distance { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Heart_Rate { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Slope { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Used_Time { get; set; }

    /// <summary>
    /// ;
    /// </summary>
    public int Interrupt_Type { get; set; }
}

GPS坐标转换类 一开始用的是一个叫YaDong.GPS.Converter的NuGet包,结果发现转换的坐标怎么都不对,然后直接F12看源码怎么写的,当时已经检索了好几个写了算法的网页,发现就是抄的GitHub上一个古早的项目,结果还抄错了,改了别人的参数,还改反了,离了大普。。 特此说明,目前流传的最早坐标转换的算法 是 2013年GitHub的项目 :https://github.com/Leask/EvilTransform ,最早只有两个版本的代码,C#和Java的,上面项目Readme页面记录了最早来自 coodplex和googlecode,这两个代码托管平台已经打不开了。后面用的人最多的应该是 https://github.com/googollee/eviltransform ,包含了C语音版本,Python版本,Go语言版本,Php,Matlab,rush,swift等。

点击查看代码
//
// 摘要:
//     GPS坐标系统类型
public enum GpsEnum
{
    //
    // 摘要:
    //     World Geodetic System 1984 国际上通用的GPS坐标系(地心坐标系) 例如: 普通GPS定位设备采集的原始坐标系
    WGS84,
    //
    // 摘要:
    //     GCJ-02是由中国国家测绘局制订的地理信息系统的坐标系统 例如:腾讯地图、高德地图等
    GCJ02,
    //
    // 摘要:
    //     百度地图专用坐标系 例如: 百度地图
    BD09,
    //
    // 摘要:
    //     墨卡托坐标系 例如:ArcGis
    Mercator
}
//
// 摘要:
//     通用GPS坐标点
public class GpsPoint
{
    private static readonly double a = 6378245.0;

    private static readonly double ee = 0.0066934216229659433;

    private static readonly double bd_pi = 52.359877559829883;

    //
    // 摘要:
    //     纬度
    public double Latitude { get; set; }

    //
    // 摘要:
    //     经度
    public double Longitude { get; set; }

    //
    // 摘要:
    //     GPS坐标系统
    public GpsEnum Type { get; set; }

    //
    // 摘要:
    //     通用GPS坐标点构造函数
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    //   type:
    //     GPS坐标系统
    public GpsPoint(double latitude, double longitude, GpsEnum type)
    {
        Latitude = latitude;
        Longitude = longitude;
        Type = type;
    }

    //
    // 摘要:
    //     获取WGS84的坐标点
    //
    // 返回结果:
    //     转换后的位置信息
    public GpsPoint GetWGS84()
    {
        switch( Type)
        {
            case GpsEnum.GCJ02:
                return GCJ02_To_WGS84(Latitude, Longitude);
                break;
            case GpsEnum.BD09:
                return BD09_To_WGS84(Latitude, Longitude);
                break;
            case GpsEnum.Mercator:
                return Mercator_To_WGS84(Latitude, Longitude);
                default: return null;
        };
    }

    //
    // 摘要:
    //     获取GCJ02的坐标点
    //
    // 返回结果:
    //     转换后的位置信息
    public GpsPoint GetGCJ02()
    {
        switch (Type)
        {
            case GpsEnum.WGS84:
                return WGS84_To_GCJ02(Latitude, Longitude);
                break;
            case GpsEnum.BD09:
                return BD09_To_GCJ02(Latitude, Longitude);
                break;
            case GpsEnum.Mercator:
                return Mercator_To_WGS84(Latitude, Longitude).GetGCJ02();
                default: return null;
        };
    }

    //
    // 摘要:
    //     获取BD09的坐标点
    //
    // 返回结果:
    //     转换后的位置信息
    public GpsPoint GetBD09()
    {
        switch (Type)
        {
            case GpsEnum.WGS84:
                return WGS84_To_BD09(Latitude, Longitude);
                break;
                case GpsEnum.GCJ02:
                return GCJ02_To_BD09(Latitude, Longitude);
                break;
                case GpsEnum.Mercator:
                return Mercator_To_WGS84(Latitude, Longitude).GetBD09();
                default: return null;
        };
    }

    //
    // 摘要:
    //     获取墨卡托坐标
    //
    // 返回结果:
    //     转换后的位置信息
    public GpsPoint GetMercator()
    {
        switch(Type )
        {
            case GpsEnum.WGS84:
                return WGS84_To_Mercator(Latitude, Longitude);
                break;
                case GpsEnum.GCJ02:
                return GCJ02_To_WGS84(Latitude, Longitude).GetMercator();
                    case GpsEnum.BD09:
                return BD09_To_WGS84(Latitude, Longitude).GetMercator();
                default: return null;
        };
    }

    //
    // 摘要:
    //     是否超出中国境内
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     bool
    private static bool OutOfChina(double latitude, double longitude)
    {
        if (longitude < 72.004 || longitude > 137.8347)
        {
            return true;
        }

        if (latitude < 0.8293 || latitude > 55.8271)
        {
            return true;
        }

        return false;
    }

    private static double TransformLat(double latitude, double longitude)
    {
        double num = -100.0 + 2.0 * latitude + 3.0 * longitude + 0.2 * longitude * longitude + 0.1 * longitude * latitude + 0.2 * Math.Sqrt(Math.Abs(latitude));
        num += (20.0 * Math.Sin(6.0 * latitude * Math.PI) + 20.0 * Math.Sin(2.0 * latitude * Math.PI)) * 2.0 / 3.0;
        num += (20.0 * Math.Sin(longitude * Math.PI) + 40.0 * Math.Sin(longitude / 3.0 * Math.PI)) * 2.0 / 3.0;
        return num + (160.0 * Math.Sin(longitude / 12.0 * Math.PI) + 320.0 * Math.Sin(longitude * Math.PI / 30.0)) * 2.0 / 3.0;
    }

    private static double TransformLon(double latitude, double longitude)
    {
        double num = 300.0 + latitude + 2.0 * longitude + 0.1 * latitude * latitude + 0.1 * longitude * latitude + 0.1 * Math.Sqrt(Math.Abs(latitude));
        num += (20.0 * Math.Sin(6.0 * latitude * Math.PI) + 20.0 * Math.Sin(2.0 * latitude * Math.PI)) * 2.0 / 3.0;
        num += (20.0 * Math.Sin(latitude * Math.PI) + 40.0 * Math.Sin(latitude / 3.0 * Math.PI)) * 2.0 / 3.0;
        return num + (150.0 * Math.Sin(latitude / 12.0 * Math.PI) + 300.0 * Math.Sin(latitude / 30.0 * Math.PI)) * 2.0 / 3.0;
    }

    //
    // 摘要:
    //     国际通用坐标系 (WGS84) to 火星坐标系 (GCJ-02) World Geodetic System ==> Mars Geodetic System
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint WGS84_To_GCJ02(double latitude, double longitude)
    {
        double num = TransformLat(longitude - 105.0, latitude - 35.0);
        double num2 = TransformLon(longitude - 105.0, latitude - 35.0);
        double d = latitude / 180.0 * Math.PI;
        double num3 = Math.Sin(d);
        num3 = 1.0 - ee * num3 * num3;
        double num4 = Math.Sqrt(num3);
        num = num * 180.0 / (a * (1.0 - ee) / (num3 * num4) * Math.PI);
        num2 = num2 * 180.0 / (a / num4 * Math.Cos(d) * Math.PI);
        double latitude2 = latitude + num;
        double longitude2 = longitude + num2;
        return new GpsPoint(latitude2, longitude2, GpsEnum.GCJ02);
    }

    //
    // 摘要:
    //     火星坐标系 (GCJ-02) to 国际通用坐标系 (WGS84) Mars Geodetic System ==> World Geodetic System
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint GCJ02_To_WGS84(double latitude, double longitude)
    {
        double num = TransformLat(longitude - 105.0, latitude - 35.0);
        double num2 = TransformLon(longitude - 105.0, latitude - 35.0);
        double d = latitude / 180.0 * Math.PI;
        double num3 = Math.Sin(d);
        num3 = 1.0 - ee * num3 * num3;
        double num4 = Math.Sqrt(num3);
        num = num * 180.0 / (a * (1.0 - ee) / (num3 * num4) * Math.PI);
        num2 = num2 * 180.0 / (a / num4 * Math.Cos(d) * Math.PI);
        double num5 = latitude + num;
        double num6 = longitude + num2;
        return new GpsPoint(latitude * 2.0 - num5, longitude * 2.0 - num6, GpsEnum.WGS84);
    }

    //
    // 摘要:
    //     火星坐标系 (GCJ-02) to 百度坐标系 (BD-09)
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint GCJ02_To_BD09(double latitude, double longitude)
    {
        double num = Math.Sqrt(longitude * longitude + latitude * latitude) + 2E-05 * Math.Sin(latitude * bd_pi);
        double d = Math.Atan2(latitude, longitude) + 3E-06 * Math.Cos(longitude * bd_pi);
        double longitude2 = num * Math.Cos(d) + 0.0065;
        double latitude2 = num * Math.Sin(d) + 0.006;
        return new GpsPoint(latitude2, longitude2, GpsEnum.BD09);
    }

    //
    // 摘要:
    //     火星坐标系 (GCJ-02) to 百度坐标系 (BD-09)
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint BD09_To_GCJ02(double latitude, double longitude)
    {
        double num = longitude - 0.0065;
        double num2 = latitude - 0.006;
        double num3 = Math.Sqrt(num * num + num2 * num2) - 2E-05 * Math.Sin(num2 * bd_pi);
        double d = Math.Atan2(num2, num) - 3E-06 * Math.Cos(num * bd_pi);
        double longitude2 = num3 * Math.Cos(d);
        double latitude2 = num3 * Math.Sin(d);
        return new GpsPoint(latitude2, longitude2, GpsEnum.GCJ02);
    }

    //
    // 摘要:
    //     百度坐标系 (BD-09) to 国际通用坐标系 (WGS84)
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint BD09_To_WGS84(double latitude, double longitude)
    {
        GpsPoint gpsPoint = BD09_To_GCJ02(latitude, longitude);
        return GCJ02_To_WGS84(gpsPoint.Latitude, gpsPoint.Longitude);
    }

    //
    // 摘要:
    //     国际通用坐标系 (WGS84) to 百度坐标系 (BD-09)
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint WGS84_To_BD09(double latitude, double longitude)
    {
        GpsPoint gpsPoint = WGS84_To_GCJ02(latitude, longitude);
        return GCJ02_To_BD09(gpsPoint.Latitude, gpsPoint.Longitude);
    }

    //
    // 摘要:
    //     MercatorToWGS84 墨卡托转WGS84
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint Mercator_To_WGS84(double latitude, double longitude)
    {
        latitude /= 3606751501.2;
        longitude /= 3606751501.2;
        latitude = 180.0 / Math.PI * (2.0 * Math.Atan(Math.Exp(latitude * Math.PI / 180.0)) - Math.PI / 2.0);
        return new GpsPoint(latitude, longitude, GpsEnum.WGS84);
    }

    //
    // 摘要:
    //     WGS84ToMercator WGS84转墨卡托
    //
    // 参数:
    //   latitude:
    //     纬度
    //
    //   longitude:
    //     经度
    //
    // 返回结果:
    //     转换后的位置信息
    public static GpsPoint WGS84_To_Mercator(double latitude, double longitude)
    {
        longitude *= 111319.49077777777;
        latitude = Math.Log(Math.Tan((90.0 + latitude) * Math.PI / 360.0)) / (Math.PI / 180.0);
        latitude *= 111319.49077777777;
        return new GpsPoint(latitude, longitude, GpsEnum.Mercator);
    }
}

由于用的是控制台程序,简单粗暴,直接加载Cearch.db,按返回的记录表循环获取轨迹点记录就行了。

Program类

点击查看代码
 internal class Program
 {
     public static string conn = @"Data Source=C:\Users\john\Desktop\MouseWithoutBorders\Cearch.db;";
     static void Main(string[] args)
     {
         GpsPoint point = new GpsPoint(31.1774276,121.5272106,GpsEnum.WGS84);//测试坐标,通过对比高德的坐标转换API看结果,经过高德API的对比,基本上到了点后第五位相同,部分坐标第6位也相同
         var newPoint = point.GetGCJ02();
         Console.WriteLine(newPoint.Latitude+"  "+newPoint.Longitude);

         if(args.Length > 0 )
         {
             var sourceGpx = args[0];
             Console.WriteLine("正在读取gpx文件:"+sourceGpx);
             if(sourceGpx != null ) {
                 if (!sourceGpx.Contains(".gpx"))
                 {
                     Console.WriteLine("请输入gpx文件名称!");
                     Console.ReadKey();
                     return;
                 }
                 if(File.Exists(sourceGpx))
                 {
                     GPXConverWCGtoGCJ(sourceGpx);
                     Console.WriteLine("转换成功!输出文件为:GCJ_02_"+sourceGpx);
                     Console.ReadKey();
                     return;
                 }
                 else
                 {
                     Console.WriteLine($"文件{sourceGpx}不存在!");
                     Console.ReadKey();

                 }
             }
             Console.WriteLine("输入参数为空!");
             Console.ReadKey();
             return;
         }

         Dictionary<RecordDetails, List<RecordPoints>> records = new Dictionary<RecordDetails, List<RecordPoints>>();
         var items = GetAllRecordDetails();
         Console.WriteLine($"总找到{items.Count}条数据。");
         Console.WriteLine("前十条数据为:");
         //foreach(var item in items.Take(10) )
         foreach(var item in items.Take(1000))
             {
             //Console.WriteLine($"{item._Id},开始时间:{DateTimeOffset.FromUnixTimeSeconds(item.Start_Time).ToLocalTime().ToString("g")},距离 {item.Distance} 米。用时 {new TimeSpan(0,0,item.Duration).ToString(@"hh\:mm\:ss")} ");
             var points = GetRecordPointsByDetailId(item._Id);
             if(points.Count > 0 )
             {
                 //Console.WriteLine($"获取到轨迹点 {points.Count} 个。");
                 records.Add(item, points);
             }
         }
         Console.WriteLine($"包含轨迹点的记录条数 {records.Keys.Count}.");
         foreach (var key in records.Keys)
         {
             var points = records[key];
             GpsData gpsData = new GpsData();
             var recordTime = DateTimeOffset.FromUnixTimeSeconds(key.Start_Time).ToLocalTime().DateTime;
             gpsData.Tracks.Add(new Track());
             gpsData.Tracks[0].Segments.Add(new  TrackSegment());
             foreach (var record in points)
             {
                 //Gpx11Serializer gpx11Serializer = new Gpx11Serializer();
                 //gpsData.Waypoints.Add(new Waypoint(record.Latitude, record.Longitude, record.Altitude, recordTime.AddSeconds(record.Time)));
                 //gpsData.Tracks[0].Segments[0].Waypoints.Add(new Waypoint(record.Latitude, record.Longitude, record.Altitude, recordTime.AddSeconds(record.Time)));

                 //用GCJ-02格式输出
                 point = new GpsPoint(record.Latitude, record.Longitude, GpsEnum.WGS84);
                 newPoint = point.GetGCJ02();
                 gpsData.Tracks[0].Segments[0].Waypoints.Add(new Waypoint(newPoint.Latitude, newPoint.Longitude, record.Altitude, recordTime.AddSeconds(record.Time)));
                 //gpx11Serializer.Serialize(gpsData);
             }
             string content = gpsData.ToGpx(2);
             using (FileStream fs = new FileStream(recordTime.ToString("GCJ_yyyy-MM-dd_HH-mm-ss") + ".gpx", FileMode.OpenOrCreate, FileAccess.Write))
             {
                 byte[] buffer = Encoding.UTF8.GetBytes(content);
                 fs.Write(buffer, 0, buffer.Length);
             }
         }
         Console.ReadLine();
     }

     public static List<RecordDetails> GetAllRecordDetails()
     {

         using (IDbConnection cnn = new SQLiteConnection(conn))
         {
             cnn.Open();

             //var output = cnn.Query<RecordDetails>("select * from Record_Details ORDER BY distance desc LIMIT 10").ToList();
             var output = cnn.Query<RecordDetails>("select * from Record_Details ORDER BY distance desc").ToList();

             return output.ToList();
         }
     }

     public static List<RecordPoints> GetRecordPointsByDetailId(int detailId)
     {

         using (IDbConnection cnn = new SQLiteConnection(conn))
         {
             cnn.Open();

             //var output = cnn.Query<RecordDetails>("select * from Record_Details ORDER BY distance desc LIMIT 10").ToList();
             var output = cnn.Query<RecordPoints>("select * from record_points where recorddetail_id = "+detailId+" ORDER BY time").ToList();

             return output.ToList();
         }
     }


     public static void GPXConverWCGtoGCJ(string pathGpx)
     {
         //var gpxFileStr = File.ReadAllText(pathGpx);
         GpsData gpxData = new GpsData();
         using(FileStream fs = new FileStream(pathGpx, FileMode.Open, FileAccess.Read))
         {

             Gpx11Serializer gpx11Serializer = new Gpx11Serializer();
             gpxData = gpx11Serializer.DeSerialize(new StreamWrapper(fs));
         }

         foreach (var wcgPoint in gpxData.Tracks[0].Segments[0].Waypoints)
         {
             GpsPoint point = new GpsPoint(wcgPoint.Coordinate.Latitude, wcgPoint.Coordinate.Longitude, GpsEnum.WGS84);
             point = point.GetGCJ02();
             wcgPoint.Point = new Geo.Geometries.Point(point.Latitude, point.Longitude);
         }
         string content = gpxData.ToGpx(2);
         using (FileStream fs = new FileStream("GCJ-02_"+pathGpx, FileMode.OpenOrCreate, FileAccess.Write))
         {
             byte[] buffer = Encoding.UTF8.GetBytes(content);
             fs.Write(buffer, 0, buffer.Length);
         }

     }
 }

参考资料

无Root修改应用内部存储数据
利用adb备份app的数据
ADB读取和备份安卓应用数据(无Root)
解密备份出来的ab文件工具,abe
制作轨迹点叠加地图视频
telemetry-overlay(收费,大概1k人名币)
佳明Virb编辑器 已经停止更新了,最新的是18年发布的,无法加载地图
gpx2video C项目 未测试
版本最全的坐标转换
github上流传最早的坐标转换版本
Dapper
Geo

posted @ 2026-03-20 17:39  BlusJohn  阅读(14)  评论(0)    收藏  举报