专栏首页C++核心准则原文翻译自学HarmonyOS应用开发(54)- 校正定位偏差

自学HarmonyOS应用开发(54)- 校正定位偏差

上一篇文章已经介绍了如果获取当前所在位置的方法,这种方法存在一个问题:和实际位置之前存在500米左右的偏差。

原因调查

经过一番调查,结论是gps信号使用的是WGS-84坐标系,而高德地图使用的是GCJ-02火星坐标系,只有经过坐标变换才能显示正确的位置。这方面的文章网上有很多,这里采用以下博客文章中的代码:

https://www.cnblogs.com/blogger-Li/p/11616835.html

代码如下:

// 本代码引用自Hugo_nice下面的博客文章
// https://www.cnblogs.com/blogger-Li/p/11616835.html
package xwg.harmony.stopwatch;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * gps纠偏算法,适用于google,高德体系的地图
 */
public abstract class GpsUtil {

    private final static double a = 6378245.0;
    private final static double pi = 3.1415926535897932384626;
    private final static double ee = 0.00669342162296594323;

    /**
     * 计算地球上任意两点(经纬度)距离
     *
     * @param lat1
     *            第一点纬度
     * @param lng1
     *            第一点经度
     * @param lat2
     *            第二点纬度
     * @param lng2
     *            第二点经度
     * @return 返回距离 单位:米
     */
    public static double distance(double lat1, double lng1, double lat2, double lng2) {
        double a, b, R;
        R = 6378137; // 地球半径
        lat1 = lat1 * Math.PI / 180.0;
        lat2 = lat2 * Math.PI / 180.0;
        a = lat1 - lat2;
        b = (lng1 - lng2) * Math.PI / 180.0;
        double d;
        double sa2, sb2;
        sa2 = Math.sin(a / 2.0);
        sb2 = Math.sin(b / 2.0);
        d = 2 * R * Math.asin(Math.sqrt(sa2 * sa2 + Math.cos(lat1) * Math.cos(lat2) * sb2 * sb2));
        return d;
    }

    /**
     * Description: WGS-84 to GCJ-02 <BR>     *
     * @author dsn
     * @date 2017年10月24日 下午2:09:27
     * @param latitude
     *            纬度
     * @param longitude
     *            经度
     * @return [纬度,经度]
     * @version 1.0
     */
    public static double[] toGCJ02Point(double latitude, double longitude) {
        double[] dev = calDev(latitude, longitude);
        double retLat = latitude + dev[0];
        double retLon = longitude + dev[1];
        return new double[] { retLat, retLon };
    }

    /**
     * Description: WGS-84 to GCJ-02 <BR>     *
     * @author dsn
     * @date 2017年10月24日 下午2:09:27
     * @param latitude
     *            纬度
     * @param longitude
     *            经度
     * @param scale
     *            经纬度保留小数位数
     * @return [纬度,经度]
     * @version 1.0
     */
    public static double[] toGCJ02Point(double latitude, double longitude, int scale) {
        double[] dev = calDev(latitude, longitude);
        double retLat = latitude + dev[0];
        double retLon = longitude + dev[1];
        return new double[] { new BigDecimal(retLat).setScale(scale, RoundingMode.DOWN).doubleValue(),
                new BigDecimal(retLon).setScale(scale, RoundingMode.DOWN).doubleValue() };
    }

    /**
     * Description:GCJ-02 to WGS-84 <BR>     *
     * @author dsn
     * @date 2017年10月24日 下午2:09:54
     * @param latitude
     *            纬度
     * @param longitude
     *            经度
     * @return [纬度,经度]
     * @version 1.0
     */
    public static double[] toWGS84Point(double latitude, double longitude) {
        double[] dev = calDev(latitude, longitude);
        double retLat = latitude - dev[0];
        double retLon = longitude - dev[1];
        dev = calDev(retLat, retLon);
        retLat = latitude - dev[0];
        retLon = longitude - dev[1];
        return new double[] { retLat, retLon };
    }

    private static double[] calDev(double wgLat, double wgLon) {
        if (isOutOfChina(wgLat, wgLon)) {
            return new double[] { 0, 0 };
        }
        double dLat = calLat(wgLon - 105.0, wgLat - 35.0);
        double dLon = calLon(wgLon - 105.0, wgLat - 35.0);
        double radLat = wgLat / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        return new double[] { dLat, dLon };
    }

    private static boolean isOutOfChina(double lat, double lon) {
        if (lon < 72.004 || lon > 137.8347)
            return true;
        if (lat < 0.8293 || lat > 55.8271)
            return true;
        return false;
    }

    private static double calLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double calLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }
}

偏移调整

public void setLocation(double long_deg, double lat_deg){
    double ret[] = GpsUtil.toGCJ02Point(lat_deg, long_deg);
    latitude = ret[0];
    longitude = ret[1];
    invalidate();
}

在设定经纬度之前,调用转换方法即可。经过转换之后的位置和实际位置之间的偏差可以在几十米范围之内。

参考代码

完整代码可以从以下链接下载:

https://github.com/xueweiguo/Harmony/tree/master/StopWatch

参考资料

GCJ-02火星坐标系和WGS-84坐标系转换关系:

https://www.cnblogs.com/blogger-Li/p/11616835.html

获取设备的位置信息:

https://developer.harmonyos.com/cn/docs/documentation/doc-guides/device-location-info-0000000000031900

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-07-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 自学HarmonyOS应用开发(66)- 自定义布局(1)

    Harmony应用开发文档中为Java开发者提供了6种UI布局,可以满足开发者的大部分需求。但是有一个问题是:这些布局一旦显示,用户便无法进行调整。我们开发一个...

    面向对象思考
  • 自学HarmonyOS应用开发(67)- 自定义布局(2)

    除了这两个限制之外,调整对象组件的类型/个数,分隔的组件的高度都可以任意指定。够灵活了吧。

    面向对象思考
  • 自学HarmonyOS应用开发(47)- 自定义switch组件

    HarmonyOS应用开发都会用到各种各样的UI组件,开发者可以根据需求在布局文件设定UI组件各种属性。但是需求是多种多样

    面向对象思考
  • 自学HarmonyOS应用开发(53)- 获取当前位置

    在registerLocationEvent方法用来注册一个单次定位事件请求;在定位事件响应对象中我们将获得的位置信息通知给地图对象。

    面向对象思考
  • HarmonyOS简介

    前两天,华为发布了HarmonyOS 2.0,俺也赶个时髦,给大家简单介绍下HarmonyOS。

    xiangzhihong
  • 技术分析 | HarmonyOS到底是不是Android套皮?

    最近鸿蒙系统关注度好高,支持与反对、看好和看衰、「自主的全场景分布式系统」和「Android套壳」各执一词,吵的不可开交。

    刘盼
  • HarmonyOS与Android的全面对比

    第二是我个人非常看好鸿蒙系统的未来,清楚明白华为和一些民族企业担负的责任和国人的期待,虽然带着一些民族感情;鸿蒙刚发布的时候自己是非常激动的,但是后来项目太忙一...

    肉眼品世界
  • 十问华为HarmonyOS:开源一个月,开发者生态建设进度如何?

    基础软件的开源面临着非常大的挑战,除了技术研发困难重重,开源生态和社区的建设更是难上加难。9 月 10 日,在华为开发者大会 2020 上,华为消费者业务 CE...

    深度学习与Python
  • 程序员看华为HarmonyOS首发

    HarmonyOS代码正式开源,9月10日下午朋友圈散布着这条消息,科技圈炸锅了。各种声音的都有,我也挺好奇的,目前Android、iOS一统江湖,Harmon...

    马上就说
  • 【第22期】HarmonyOS应用开发(基础篇)

    这不就是说,以后华为手机都是鸿蒙系统了嘛?鸿蒙还发出了一条视频,视频中显示2021年6月2号将开启鸿蒙操作系统及华为全场景新品发布会。预计现在支持EMUI11升...

    siberiawolf
  • 【HarmonyOS 专题】02 搭建简单登录页面

    和尚在搭建完 HarmonyOS 环境之后,有很长时间没有研究过 HarmonyOS,DevEco Studio 已经更新了多个版本,和尚在升级完 ID...

    阿策小和尚
  • 华为鸿蒙生态的目标:市占率16%以上,跨过生死线

    9 月 10 日,华为正式推出 鸿蒙 OS 2.0 版本(HarmonyOS 2.0),并宣布将 Harmony OS 的代码捐赠给开放原子开源基金会进行开源孵...

    深度学习与Python
  • 前端视角看HarmonyOS

    公元 2021 年 6 月 2 日,【 HarmonyOS2.0 】正式发布,以 JavaScript 作为 IoT 应用开发的架构语言,这是继 SpaceX ...

    公众号@魔术师卡颂
  • 带你了解鸿蒙开发基本流程

    2020年9月10号,鸿蒙2.0(HarmonyOS 2.0)系统正式发布,鸿蒙2.0面向应用开发者发布Beta版本,

    Python进击者
  • HarmonyOS-对Android开发者也太友好了吧

    2020年9月10日,华为消费者业务软件部总裁王成录又一次站在了松山湖华为开发者大会的主舞台上。今年,他带来了万众瞩目的华为鸿蒙HarmonyOS2.0...

    Android扫地僧
  • 搭建鸿蒙编译环境(VMware+Linux)

    分布式软总线是多种终端设备的统一基座,为设备之间的互联互通提供了统一的分布式通信能力,能够快速发现并连接设备,高效地分发任务和传输数据。分布式软总线示意图见。

    跋扈洋
  • 【HarmonyOS 专题】01 基础 Mac 环境安装配置

    HarmonyOS 已于 2020 年 12 更新到 2.0 版本;和尚周围的人都在学习和研究,和尚也想学习一下;今天和尚从 0 开始学习,第一步简单介绍基础...

    阿策小和尚
  • 研究人员提出针对GPT-3等语言模型的偏差修正

    快速学习或从几个示例中学习任务的能力是人工智能的关键与强项。像OpenAI的GPT-3这样的大型AI自然语言模型无需精调即可执行多次学习。

    大数据文摘
  • 牛掰了!鸿蒙与Android完美融合,将鸿蒙设备当Android设备用

    1. 你看着是鸿蒙,其实它是Android,你看着是Android,其实它是鸿蒙

    蒙娜丽宁

扫码关注云+社区

领取腾讯云代金券