# SuperMap-XR眼镜室内导航示例 **Repository Path**: middle-aged-people-learn-new/xr-glasses-indoor-navigation ## Basic Information - **Project Name**: SuperMap-XR眼镜室内导航示例 - **Description**: Unity与AS联合开发的方式,基于影创XR眼镜的SDK,实现室内导航的示例。 - **Primary Language**: Unknown - **License**: EPL-1.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2024-02-24 - **Last Updated**: 2024-02-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # XR眼镜室内导航示例 #### 介绍 使用Unity3d联合AS的开发方式,通过调用 [SuperMap iMobile](http://support.supermap.com.cn/product/iMobile.aspx)的路径分析功能,实现XR眼镜的室内导航。 ##### 设备型号 XR眼镜:JIMO 移动手机:使用支持ARCore/AREngine移动手机(如华为、小米等)。 #### 项目说明 ##### SuperMap许可 1、在官网申请试用许可。 2、将试用许可(两个license文件)拷贝至手机的根目录/SuperMap/license下 ##### 路网数据的制作 1、使用SuperMap iDesktopX制作路网数据(udb数据源以及swmu工作空间) 2、将制作完成的路网数据拷贝至手机的根目录/Supermap/arglasses/ ![路网数据](https://file.eqgis.cn/img/20220329/data01.png) ##### Unity的环境 1、开发平台选择Android 2、配置相应的SDK\NDK\JDK 3、导入master\01UnityAssets\下的unitypackage (Demo.unitypackage为本导航示例的资源,SDK_Foundation.unitypackage为[影创SDK](https://developer.shadowcreator.com)) ##### 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](https://developer.shadowcreator.com) 说明:离线jar与so位于master\03离线依赖,so库和离线jar在[SupMap](http://support.supermap.com.cn/DownloadCenter/DownloadPage.aspx?id=1872)下载。 ##### Unity端 1、创建unity项目,切换为Android平台 ![](https://file.eqgis.cn/img/20220329/01.png) 2、导入影创SDK(SDK_Foundation.unitypackage) Assets->Import Package->Custom Package ![](https://file.eqgis.cn/img/20220329/02.png) 变更为: ![](https://file.eqgis.cn/img/20220329/03.png) 3、若控制台报错,提示缺少'Newtonsoft',则打开对应脚本,根据提示导入 [参考链接](https://docs.microsoft.com/zh-cn/visualstudio/gamedev/unity/unity-scripting-upgrade?view=vs-2019) 4、打开scene ![](https://file.eqgis.cn/img/20220329/04.png) ##### 导出安卓工程 1、File->Build Settings->Player Settings 勾选使用自定义的gradle设置 ![](https://file.eqgis.cn/img/20220329/05.png) 2、在Assetes/Plugins/Android/目录下,找到baseProjectTemplate.gradle, ```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, ```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下 ![](https://file.eqgis.cn/img/20220329/06.png) 3、连接设备后,点击“run launcher”运行 ##### 使用流程 在场景中点击面板上的名称,则在场景中生成对应的导航路径。 ![](https://file.eqgis.cn/img/20220329/res01.png) ##### 手机端效果 [视频链接](http://tc.eqgis.cn:10607/%E5%85%B6%E5%AE%83/AR%E7%9C%BC%E9%95%9C%E8%BF%81%E7%A7%BB%E6%89%8B%E6%9C%BA%E5%BD%95%E5%88%B6%E6%95%88%E6%9E%9C.mp4) #### 关键代码 ##### MainActivity 申请权限->加载离线许可->读取工作空间->载入数据 ```java @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端调用。 ```java 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 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路径分析示例](http://support.supermap.com.cn/DataWarehouse/WebDocHelp/iMobileForAndroid/SuperMapObjectsEmbeddedHelp.htm) 搜索“路径分析” [SuperMap iMobile 交通网络分析结果类](http://support.supermap.com.cn/DataWarehouse/WebDocHelp/iMobileForAndroid/SuperMapObjectsEmbeddedHelp.htm) 搜索“TransportationAnalystResult” ```java /** * 获取路径 * @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()创建路径。 ```c# 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("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("pathAnalyst", currentP.x, currentP.z, destinationP.x, destinationP.z); //将字节组转为字符串 string jsonStr = System.Text.Encoding.UTF8.GetString(resultBytes); pathMonoBehaviour.ClearPath(); pathMonoBehaviour.AddPath(jsonStr); } } ```