2022-11-09-XR眼镜室内导航示例

写在前面的话

  • 本文档仅供参考,相关描述不一定准确
  • 码云地址:链接

介绍

使用Unity3d联合AS的开发方式,通过调用 SuperMap iMobile的路径分析功能,实现XR眼镜的室内导航。

设备型号

XR眼镜:JIMO

移动手机:使用支持ARCore/AREngine移动手机(如华为、小米等)。

项目说明

SuperMap许可

1、在官网申请试用许可。
2、将试用许可(两个license文件)拷贝至手机的根目录/SuperMap/license下

路网数据的制作

1、使用SuperMap iDesktopX制作路网数据(udb数据源以及swmu工作空间)
2、将制作完成的路网数据拷贝至手机的根目录/Supermap/arglasses/
路网数据

Unity的环境

1、开发平台选择Android

2、配置相应的SDK\NDK\JDK

3、导入master\01UnityAssets\下的unitypackage

(Demo.unitypackage为本导航示例的资源,SDK_Foundation.unitypackage为影创SDK

AndroidStudio

AndroidStudio version > 3.6
gradle version > 5.6.1

说明:master\02AndroidStudioProj为本示例对应的AS工程,是通过Unity导出得到,

再在该AS工程上打包apk(此处涉及,AS与Unity的相互调用,具体实现见后续“关键代码”)。

重现流程

依赖
so库

libimb2d_v1021.so

离线jar

com.supermap.data_v1021.jar

com.supermap.ar_v1021.jar

com.supermap.analyst_v1021.jar

gson-2.1.jar

unitypackage

影创SDK

说明:离线jar与so位于master\03离线依赖,so库和离线jar在SupMap下载。

Unity端

1、创建unity项目,切换为Android平台

2、导入影创SDK(SDK_Foundation.unitypackage)
Assets->Import Package->Custom Package

变更为:

3、若控制台报错,提示缺少'Newtonsoft',则打开对应脚本,根据提示导入
参考链接
4、打开scene

导出安卓工程

1、File->Build Settings->Player Settings

勾选使用自定义的gradle设置

2、在Assetes/Plugins/Android/目录下,找到baseProjectTemplate.gradle,

allprojects {
    buildscript {
        repositories {**ARTIFACTORYREPOSITORY**
            google()
            jcenter()
            //添加的内容
            maven{
                url "https://repo.eqgis.cn"
            }
            maven {
                url "https://developer.huawei.com/repo/"
            }
...
    }

3、在Assetes/Plugins/Android/目录下,找到mainTemplate.gradle,

...
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //添加内容如下
    api 'com.eqgis:sceneform-sm:1.19.10'
    api 'com.eqgis:eqtool:1.2.2'
**DEPS**}
...

4、File->Build Settings
勾选Export Project,点击“Add Open Scenes”,再点击“Export”,选择导出路径后直接导出AS工程。

AS端打包

1、打开上一步骤导出的工程
2、将master\02AndroidStudioProj中的java、res、so拷贝至unityLibrary下

3、连接设备后,点击“run launcher”运行

使用流程

在场景中点击面板上的名称,则在场景中生成对应的导航路径。

手机端效果

视频链接

关键代码

MainActivity

申请权限->加载离线许可->读取工作空间->载入数据

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.e(TAG, "onCreate: ");
        //载入Unity场景
        super.onCreate(savedInstanceState);
        Utils.requestPermissions(this);
		//加载SuperMap许可
        Environment.setLicensePath(sdCard + "/SuperMap/license/");
        Environment.initialization(this);

        //加载工作空间
        WorkspaceConnectionInfo info = new WorkspaceConnectionInfo();
        info.setType(WorkspaceType.SMWU);
        info.setServer(sdCard + "/SuperMap/arglasses/arglasses.smwu");
        workspace = new Workspace();
        boolean open = workspace.open(info);
        if (!open){
            Toast.makeText(this, "工作空间打开失败,\n请检查数据是否存在!", Toast.LENGTH_SHORT).show();
        }

        //初始化
        Destination.getInstance().init(workspace);
    }
Destination

编写pathAnalyst()方法,供Unity端调用。

    static byte[] pathAnalyst(float startX,float startY,float endX,float endY){
        //添加偏移量:
        startX += offset[0];
        startY += offset[1];
        endX += offset[0];
        endY += offset[1];

        try {
            if (pathAnalystHelper == null){
                //传入指定数据源
                pathAnalystHelper = new PathAnalystHelper(workspace.getDatasources().get(0));
            }
        }catch (Exception e){
            Log.e("IKKYU_Destination", "pathAnalyst: ", e);
        }

        //根据起点、终点坐标生成路径
        List<Point3D> pathLocationPs = createPath(startX, startY, endX, endY);
        if (pathLocationPs == null)return null;

        //坐标转换:地理坐标=>AR坐标=>Unity坐标
        Vector3[] result = getVector3s(pathLocationPs);
        String s = new Gson().toJson(result);
        return s.getBytes(StandardCharsets.UTF_8);
    }
PathAnalystHelper

路径分析。

SuperMap iMobile路径分析示例 搜索“路径分析”

SuperMap iMobile 交通网络分析结果类 搜索“TransportationAnalystResult”

    /**
     * 获取路径
     * @param netDataName 交通网络数据集名称
     * @param m_Points 第一个点为起点,最后一个点为终点
     * @return 交通网络分析结果
     */
    public TransportationAnalystResult getPath(String netDataName, Point2Ds m_Points){
        if (m_analyst == null){
            DatasetVector datasetLine = (DatasetVector) datasource.getDatasets().get(netDataName);

            // 设置网络分析基本环境,这一步骤需要设置 分析权重、节点、弧段标识字段、容限
            TransportationAnalystSetting setting = new TransportationAnalystSetting();
            setting.setNetworkDataset(datasetLine);
            setting.setEdgeIDField(m_edgeID);
            setting.setNodeIDField(m_nodeID);
//        setting.setEdgeNameField("roadName");

            setting.setTolerance(2);

            WeightFieldInfos weightFieldInfos = new WeightFieldInfos();
            WeightFieldInfo weightFieldInfo = new WeightFieldInfo();
            weightFieldInfo.setFTWeightField("smLength");
            weightFieldInfo.setTFWeightField("smLength");
            weightFieldInfo.setName("length");
            weightFieldInfos.add(weightFieldInfo);
            setting.setWeightFieldInfos(weightFieldInfos);
            setting.setFNodeIDField("SmFNode");
            setting.setTNodeIDField("SmTNode");

            //构造交通网络分析对象,加载环境设置对象
            m_analyst = new TransportationAnalyst();
            m_analyst.setAnalystSetting(setting);
            m_analyst.load();
        }

        //分析路径
        TransportationAnalystParameter parameter = new TransportationAnalystParameter();
        parameter.setWeightName("length");

        /**
         * 必须将交通网络分析参数设置(TransportationAnalystParameter)对象的 isPathGuidesReturn 方法设置为 true,才能从分析结果中获取到行驶导引集合。
         * */
        //设置最佳路径分析的返回对象
        parameter.setPoints(m_Points);
        parameter.setNodesReturn(true);
        parameter.setEdgesReturn(true);
        parameter.setPathGuidesReturn(true);//true
        parameter.setRoutesReturn(true);
        TransportationAnalystResult path;
        try {
            path = m_analyst.findPath(parameter, false);

        }catch (Exception e){
            Log.e("MapData", "getPath: ",e);
            path = null;
        }

        return path;
    }
unity脚本

将Destination.cs绑定至对应对象。通过调用CreatePath()创建路径。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Destination : MonoBehaviour
{
    public Transform destination;//终点位置
    public Transform currentPosition;//当前位置
    public PathMonoBehaviour pathMonoBehaviour;//用于生成路径的组件

    private AndroidJavaObject ajObject;
    // Start is called before the first frame update
    void Start()
    {
        if (Application.platform != RuntimePlatform.Android) return;
        AndroidJavaClass androidJavaClass = new AndroidJavaClass("com.supermap.ar.path.Destination");
        ajObject = androidJavaClass.CallStatic<AndroidJavaObject>("getInstance");
        
    }

    // Update is called once per frame
    void Update()
    {

    }

    public void CreatePath()
    {
        //获取当前位置
        Vector3 currentP = currentPosition.position;
        //目标位置为提前写入的固定值
        Vector3 destinationP = destination.position;
        if (Application.platform != RuntimePlatform.Android) return;
        if(ajObject == null)
        {
            SuperMap.AR.Unity.IMB.ShowToast("the javaObject of Destination was null.");
            return;
        }
        SuperMap.AR.Unity.IMB.ShowToast("创建路径");


        //注意:unity的坐标系与supermap组件的Point3D的坐标系
        byte[] resultBytes = ajObject.CallStatic<byte[]>("pathAnalyst", currentP.x, currentP.z, destinationP.x, destinationP.z);

        //将字节组转为字符串
        string jsonStr = System.Text.Encoding.UTF8.GetString(resultBytes);

        pathMonoBehaviour.ClearPath();
        pathMonoBehaviour.AddPath(jsonStr);
    }
}
posted @ 2024-05-30 17:31  EQ-雪梨蛋花汤  阅读(30)  评论(0)    收藏  举报