基于地理位置的Android AR app项目简述

(一)需求分析

1、项目背景

 基于当今的社会需求,没有人再会小看营销的重要性。而二维图像和视频等营销方式已经吸引不了客户的眼球。随着支付宝AR红包和任天堂的《精灵宝可梦》游戏的火速走红,AR已经成为了营销者中的新宠儿。公司主营业务便是VR相关,但是为了更好的开展业务和增加技术储备。于是,一种新型的基于地理位置的AR App便诞生了。

2、本项目应用场景
  • 旅游景点
  • 房地产看房
  • AR地图导航
  • 其他
3、用户使用场景描述
  • 用于旅游景点
     用户来到景区,通过扫描相应的二维码,系统反馈带有AR效果的标签,比如可以看附近的公厕、停车场或者其他标志性建筑物,用户点击标签,会进入VR场景,查看相应标签做对应的景点或建筑物。
  • 用于房地产看房
     用户买房一般非现房,用户想知道建成之后什么效果。用户仅需要扫描二维码或者通过图像识别接口扫描实物,系统反馈AR效果的房间内物品标签,用户点击可查看建成后效果图(VR图片或视频展示或其他广告展示)
  • 用于地图导航
     用户使用该软件搜索目的地进行AR导航(导航部分还未开发)
4、技术&商业价值
  • 可广泛用于当前新式广告营销
  • 可储备相应技术,迎接5G时代,与5G结合
  • 为以后开发AR/VR相关产品做技术积累

(二)概要设计

1、用户操作流程图设计

下面是完整的设计思路。

在这里插入图片描述
注:该程序上述部分功能还未实现(图片识别、导航、看全景),不过已经在其他开发的APP中实现,本文主要介绍显示动态标签功能实现。

2、APP端设计

在这里插入图片描述

3、服务端设计

在这里插入图片描述

4、数据库设计

主要是由三张表组成,分别是扫描信息详情表、位置信息详情表、人员详情表。
在这里插入图片描述

(三)实现所用主要技术

1、APP端
2、服务端
  • springboot后端开发框架
  • lombok插件
  • mysql jdbc
  • maven项目管理工具
  • jpa
  • junit单元测试工具
  • IDEA
  • Java Web开发技术
3、数据库
  • MySQL5.7
  • Navicat for Mysql5.7
4、其他
  • xmind
  • processOn

(四)详细设计&实现难点

1、APP端核心模块功能分析
  • 解析后端发来的数据
     主要是json格式的解析
  /**
     * 数据解析操作
     */
    public class HandleResultData implements Runnable{
        @Override
        public  void run(){
            String RequestUrl="http://uae5f2.natappfree.cc/ar/client/location/show?scanId=1";
            JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
                    //第一个参数,请求的网址
                    RequestUrl,
                    //第二个参数
                    null,
                    //响应正确时的处理
                    new Response.Listener<JSONObject>() {
                        @Override
                        public void onResponse(JSONObject response) {
                            Log.d("TAG", response.toString());
                            /**
                             * 解析Json为Object
                             */
                            String mJSON = response.toString();
                            ResultBean resultBean=new ResultBean();
                            resultBean = JSON.parseObject(mJSON,ResultBean.class);

                    //响应错误时的处理
                    new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            Log.e("TAG", error.getMessage(), error);
                            mHandler.obtainMessage(MSG_FAILURE).sendToTarget();
                        }
                    });
            //把这个请求加到Volley队列即可
            jsonObjectRequest.setTag(TAG);
            mQueue.add(jsonObjectRequest);

        }

  • AR浮窗效果实现
     主要是将导航座标显示在浮层上以实现AR效果。
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (currentLocation == null) {
            return;
        }

        final int radius = 30;
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.WHITE);
        paint.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.NORMAL));
        paint.setTextSize(60);

        for (int i = 0; i < arPoints.size(); i ++) {
            float[] currentLocationInECEF = LocationHelper.WSG84toECEF(currentLocation);
            float[] pointInECEF = LocationHelper.WSG84toECEF(arPoints.get(i).getLocation());
            float[] pointInENU = LocationHelper.ECEFtoENU(currentLocation, currentLocationInECEF, pointInECEF);

            float[] cameraCoordinateVector = new float[4];
            Matrix.multiplyMV(cameraCoordinateVector, 0, rotatedProjectionMatrix, 0, pointInENU, 0);

            // cameraCoordinateVector[2] is z, that always less than 0 to display on right position
            // if z > 0, the point will display on the opposite
            if (cameraCoordinateVector[2] < 0) {
                float x  = (0.5f + cameraCoordinateVector[0]/cameraCoordinateVector[3]) * canvas.getWidth();
                float y = (0.5f - cameraCoordinateVector[1]/cameraCoordinateVector[3]) * canvas.getHeight();

                canvas.drawCircle(x, y, radius, paint);
                canvas.drawText(arPoints.get(i).getName(), x - (30 * arPoints.get(i).getName().length() / 2), y - 80, paint);
            }
        }
    }
2、服务端核心模块功能分析
  • 请求数据API
     我设计了一个数据请求的API格式,data链表中夹杂着一个locationlist链表,双层链表实现。
{
   "code":0,
   "msg":"success",
   "data":{
        "name": "黄河路",
        "type": 1,
              "locationList":[
                {
                  "id":"123456",
                  "name":"丹尼斯",
                  "latitude":12.335,
                  "longitude":34.225,
                  "altitude":5.2,
                  "description":"一个购物的地方",
                  "icon":"http://www.dennis.com.cn/u/cms/www/201707/191157482x37.jpg",
                  "link":"http://122.114.223.188/toShow?VRourEditor=206",
                  "people":[
                        {
                          "name":"jack",
                          "responsibility":"doctor",
                          "phone":"13052312154"
                        },
                        {
                          "name":"evan",
                          "responsibility":"护士",
                          "phone":"13565667488"
                        }
                  ]
                },
                {
                  "id":"123457",
                  "name":"微阿科技",
                  "latitude":44.123,
                  "longtitude":33.124,
                  "altitude":5.2,
                  "description":"公司",
                  "icon":"http://www.dennis.com.cn/u/cms/www/201707/191157482x37.jpg",
                  "link":"http://122.114.223.188/toShow?VRourEditor=206",
                  "people":[
                        {
                          "name":"linda",
                          "responsibility":"doctor",
                          "phone":"1325355555"
                        },
                        {
                          "name":"tim",
                          "responsibility":"护士",
                          "phone":"13561125488"
                        }
                  ]
                }
              ]
        }
 }
3、核心算法分析
  • 将gps座标转换成手机导航座标实现
    其实就是先将GPS座标即大地中心系WGS84(WorldGeodeticCoordinateSystem1984)转化为地球中心座标系ECEF(Earth-Centered, Earth-Fixed),然后再将地球中心座标系ECEF转换成局部切线平面ENU(东、北、天)座标系,再将其转化为手机导航座标系x、y轴。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

上述图片参考:无人机常见座标系


更详细的介绍我放在了我的项目中,可以参考:
https://github.com/Evanlovea/AR-App/tree/master/ARNavigation/app/src/main/res/docs

/**
     * 将GPS座标系转换为ECEF
     * (ECEF(Earth-Centered,Earth-Fixed),以地球为中心,符合地球,
     * 是一个笛卡尔座标系,也称为“普通地表”系统)
     * @param location
     * @return
     */
    public static float[] WSG84toECEF(Location location) {
        double radLat = Math.toRadians(location.getLatitude());
        double radLon = Math.toRadians(location.getLongitude());

        float clat = (float) Math.cos(radLat);
        float slat = (float) Math.sin(radLat);
        float clon = (float) Math.cos(radLon);
        float slon = (float) Math.sin(radLon);

        float N = (float) (WGS84_A / Math.sqrt(1.0 - WGS84_E2 * slat * slat));

        float x = (float) ((N + location.getAltitude()) * clat * clon);
        float y = (float) ((N + location.getAltitude()) * clat * slon);
        float z = (float) ((N * (1.0 - WGS84_E2) + location.getAltitude()) * slat);

        return new float[] {x , y, z};
    }

    /**
     * 将ECEF座标系转换为ENU(站心座标系)座标系
     *
     * @param currentLocation
     * @param ecefCurrentLocation
     * @param ecefPOI
     * @return
     */
    public static float[] ECEFtoENU(Location currentLocation, float[] ecefCurrentLocation, float[] ecefPOI) {
        double radLat = Math.toRadians(currentLocation.getLatitude());
        double radLon = Math.toRadians(currentLocation.getLongitude());

        float clat = (float)Math.cos(radLat);
        float slat = (float)Math.sin(radLat);
        float clon = (float)Math.cos(radLon);
        float slon = (float)Math.sin(radLon);

        float dx = ecefCurrentLocation[0] - ecefPOI[0];
        float dy = ecefCurrentLocation[1] - ecefPOI[1];
        float dz = ecefCurrentLocation[2] - ecefPOI[2];

        float east = -slon*dx + clon*dy;

        float north = -slat*clon*dx - slat*slon*dy + clat*dz;

        float up = clat*clon*dx + clat*slon*dy + slat*dz;

        return new float[] {east , north, up, 1};
    }
4、效果展示

 下面是某商业APP的图,我的没它那么花里胡哨的(我做的UI比较简单 ),不过原理应该差不多,不过这个APP实现应该是基于某地图的SDK的,而我的可以动态添加地理位置信息,不知道它的可不可以。可参考。(主要是我懒得搭服务器部署运行了 )。
在这里插入图片描述

5、其他模块实现

 可以参考我的github地址:基于地理位置的AR app项目

(五)参考文献

【1】http://www.gogocity.cn/flash/video2.mp4
【2】无人导航常见座标系
【3】DSTO-TN-0432.pdf
【4】Android开发技术指南

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章