产品介绍

Wish3D Earth是基于WebGL技术开发实现的三维地球渲染引擎,通过Wish3D Earth可以无需任何插件即可轻松的构建出在浏览器端可用的多功能三维数字地球,实现数据的快速共享。支持海量实景三维模型、影像、地形数据、三维模型的加载和展示;支持多种矢量数据格式的加载和展示;同时具备接入二维平台数据的能力,具备良好的扩展能力。

Wish3D Earth拥有强大的三维量测、可视域分析、体积量算、日照分析等特色功能,同时具备多种数据的场景构建与场景对比、单体化、模型压平、矢量标绘等特色应用,能够对辅助规划决策、管理规划成果数据起到重要作用。

平台采用多层架构,充分保证系统的安全性、跨平台性、易扩展性、易维护性,同时平台数据经过统一转换成为加密数据结构,在实现快速加载的同时,保证了数据的安全性。

数据层

数据层使用MySQL和MongoDB作为数据存储仓库,MySQL负责存储用户信息、权限信息、基本的场景、数据等;MongoDB负责存储大数据量的倾斜模型、地形、影像,以及一些零碎的文件数据。

服务层

服务层介绍

服务层使用Tomcat作为应用服务器,包含了三个重要的管理模块:场景管理器、用户管理器和数据管理器。场景管理器主要负责场景信息的保存与加载,包括数据图层、场景基本信息、场景视角等;用户管理器主要负责对用户的管理,包括注册用户、登陆\注销、密码服务、权限管理等;数据管理器主要负责对数据的上传、发布、处理及许可控制等。三个模块相互影响,缺一不可。

服务层开放接口API

API文档地址

API框架

应用层

应用层提供了后台管理系统和三维场景预览界面,后台管理系统提供了数据上传、场景编辑、用户管理、许可管理、以及磁盘预警等功能,作为统一的数据管理和场景管理平台;前端展示提供了场景和数据的三维可视化,并且包含丰富的三维功能,包括基础测量、三维分析、双屏对比、飞行浏览、单体化、模型压平,图层管理等。
注:应用层中的前端展示即为二次开发模块,用户可针对自身需求,定制属于自己的前端展示平台。

前端应用层有着丰富的三维数据、三维场景操作接口,基本类图结构如下:

类图结构

具体的示例代码请参考案例沙箱给出的示例。

如需查阅相关API内容,请参阅API说明文档

概述

Wish3D Earth是用于展示数据的平台,支持多种类型数据的混合加载,可对数据进行标绘、量测、分析等操作。但是这些操作必须是在场景中进行。Wish3D Earth的场景实际就是一个拟真的三维地球。如何构建一个拥有三维球的场景是开始开发的第一步。

场景构建

  1. 场景页面构建

     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>Hello World</title>
         <link href="LSGlobe/Apps/SampleCode/templates/bucket.css" type="text/css" rel="stylesheet" />
         <script type="text/javascript" src="LSGlobe/Apps/SampleCode/Sandcastle-header.js"></script>
         <script type="text/javascript" src="LSGlobe/Build/LSGlobe/LSGlobe.js"></script>
     </head>
     <body>
     <div id="lsGlobe" class="fullSize"></div>
     <div id="loadingOverlay"><h1>Loading...</h1></div>
     </body>
     </html>
    
  2. 初始化一个球

     function startup(LSGlobe) {
         'use strict';
         var viewer = new LSGlobe.Viewer('lsGlobe', {
             baseLayerPicker: false,
             sceneModePicker: false,
             fullscreenButton:false,
             guid:licenseCode,
             //许可码
             licenseUrl:lincenseUrl,
             //许可地址
         });
     }
     if (typeof LSGlobe !== "undefined") {
         startup(LSGlobe);
     } else if (typeof require === "function") {
         require(["LSGlobe"], startup);
     }
    
  3. 许可配置

    许可配置前需要有许可,没有许可场景是无法构建的。本地电脑配置好许可以后可用接口获取到许可码和许可路径。

    获取接口

    http://localhost:1000/api/license/1.0.0/getLicenseConf

    返回信息是许可路径和许可码的拼接字符串用#分个开

场景事件

1.初始化事件句柄
var handler = new LSGlobe.ScreenSpaceEventHandler(viewer.scene.canvas);
2.添加事件句柄鼠标操作类型
handler.setInputAction(function (movement){
    //movement.position当前的屏幕坐标
}, LSGlobe.ScreenSpaceEventType.LEFT_CLICK);
//LEFT_DOWN左键按下时触发
//LEFT_UP 左键弹起时触发
//LEFT_CLICK 左击时触发
//RIGHT_CLICK 右击时触发
//DOUBLE_CLICK 双击时触发
//MOUSE_MOVE 鼠标移动时触发
3.事件销毁
Handler.destroy();

场景坐标转换

1.屏幕坐标转换和世界坐标相互转换

//屏幕坐标转世界坐标
var Pos=scene.pickGlobe(movement.position);
//movement.position 鼠标事件触发时返回的坐标

//世界坐标转屏幕坐标
var movement = LSGlobe.SceneTransforms.wgs84ToWindowCoordinates(scene, pos);
//scene 场景对象 pos世界坐标
2.世界坐标和经纬度相互转换
//世界坐标转换为经纬度
var cartographic = LSGlobe.Cartographic.fromCartesian(Pos);
//Pos世界坐标对象  
var currentClickLon = LSGlobe.Math.toDegrees(cartographic.longitude);
//经度

var currentClickLat = LSGlobe.Math.toDegrees(cartographic.latitude);
//纬度

var height= cartographic.height;
//高度

//经纬度换为世界坐标转
var ellipsoid=viewer.scene.globe.ellipsoid;
直接转换
var cartographic=LSGlobe.Cartographic.fromDegrees(longitude,latitude,height);
//longitude经度 latitude维度 height高度  
var cartesian3=ellipsoid.cartographicToCartesian(cartographic);

3.经纬度弧度相互转换
经纬度转弧度
LSGlobe.Math.toRadians(degrees)

弧度转经纬度
LSGlobe.Math.toDegrees (radians)

素材

素材上传

  • 本地数据上传

    目前Wish3DEarth支持的空间数据类型:

类型 格式 上传要求
实景三维 osgb 压缩成zip格式
人工模型 obj、fbx、skp 压缩成zip格式,obj,fbx需要将纹理等附属文件一并打包
矢量数据 shp、kml 需要压缩成zip格式
影像数据 lrp lrp格式为高效的影像格式,需要通过免费的LSV将tif等文件格式转换后上传
地形数据 lrp lrp格式为高效的地形格式,需要通过免费的LSV将tif等文件格式转换后上传

素材上传前台使用插件是webuploader.js,数据的上传是分片上传的

  1. 上传素材接口

     http://localhost:8080/TX_DataManager/FileUpload/server.action
    
  2. 数据上传前需要进行md5验证

     http://localhost:8080/TX_DataManager/FileUpload/md5Valid.action
    
传入参数 参数说明
fileId 上传文件id (插件自动生成)
fileName 上传文件名称
fileExt 上传文件的类型(.zip 或.lrp)
fileType 上传文件类型 (1实景2.人工3.矢量4影像5地形)
fileSize 上传文件的大小
menuId 上传权限管理参数(固定数值)
返回参数 参数说明
guid 上传文件的唯一标识
md5_arr 一般为””
  1. 上传完成后数据进行分片合并校验

    分片校验接口

     http://localhost:8080/TX_DataManager/FileUpload/composeFile.action
    
传入参数 参数说明
dataguid 上传文件md5校验后后返回guid
fileName 上传文件名称
chunks 分片总数量
  1. 分片校验完成以后需要进行文件状态的改变,让数据在后台线程中自动处理

    修改数据状态接口

    http://localhost:1000/api/upload/1.0.0/updateTask

传入参数 参数说明
title 上传素材名称 当数据类型是skp时可转换为3dtiles类型或者是lmz类型 此时需要在title后拼上字符串#0/#1,#0为转换为lmz #1为转换为3dtiles
position 上传素材的坐标 默认是120,30,0
dataGuid 上传文件md5校验后后返回guid
handleStatus 数据的处理状态默认是1(0,创建中,1等待中,2解压中,3转换中,4入库mongodb,5入库mysql)刚上传的数据需把它加入到等待队列中
  1. 上传处理过程中如果出现(数据结构不对等原因)导致数据无法正常处理完成,并且影响其他数据上传的情况,可将该数据标记为问题数据

    标识数据为问题数据

    http://localhost:1000/api/upload/1.0.0/updateTask

  2. 上传处理对于处理不成功的数据可重新处理

    重新处理数据

    http://localhost:1000/api/upload/1.0.0/updateTask

素材管理

传入参数 参数说明
keyWord 关键字如果为""表示差全部,有值时根据关键字查询
type 根据类型来查询数据为0差全部;为1查实景数据;为2查人工数据;为3查矢量数据;为4查影像数据;为5查地形数据
dataType 查询数据的类型为0查自己拥有的;为1查全部
frompage 页面标识
menuId 权限相关字段
pageIndex 第几页
count 每页条数
返回参数 参数说明
count 总条数
data 数据列表
dataType 查询数据的类型为0查自己拥有的;为1查全部
frompage 页面标识
menuId 权限相关字段
  • 素材删除
  • 用户删除数据(预删除)

    为保证数据的误删除删除数据时需要进行验证码的验证

    获取验证码接口 http://localhost:1000/api/server/1.0.0/getValidCode

    删除数据(预删除)

    http://localhost:1000/api/server/1.0.0/delete

  • 用户删除数据(真实删除)

      该删除主要用于管理员对用户删除后的数据进行清理,该删除也需要精心验证码的验证,该删除分为两部分,一部分是删除数据的mysql和mongodb中的数据,一部分是删除源数据的zip包数据

    删除mysql和mongodb中的数据(真实删除)

    http://localhost:1000/api/server/1.0.0/deleteData

    删除zip包数据(真实删除)

    http://localhost:1000/api/server/1.0.0/deleteZip

  • 素材修改

      素材修改主要是修改素材的基本信息

    获取素材的详细信息

    http://localhost:1000/api/server/1.0.0/details

    修改素材的名称,位置

    http://localhost:1000/api/server/1.0.0/update

    修改素材的缩略图及其视角

    http://localhost:1000/api/server/1.0.0/uploadImg

    获取canvas缩略图的方法

      //一个隐藏的img标签
      var oOriginImg=$("#visibleImg");
      //获取当前canvas的宽高
      var canvasWidth=$("canvas").width();
      var canvasHeight=$("canvas").height();
      //获取当前canvas转化为图片信息  base64位的字符串
      var sImg=viewer.scene.canvas.toDataURL("image/jpeg", 0.5);
      //将图片信息加载到隐藏的img标签中
      oOriginImg.off("load");
      oOriginImg.attr("src",sImg);
      oOriginImg.attr("style","width:"+canvasWidth+"px;height:"+canvasHeight+"px;");
      oOriginImg.off("load").load(function(){
          //原始尺寸
          var orinWidth=oOriginImg.width();
          var orinHeight=oOriginImg.height();
          //计算比例
          var fProportion=parseFloat(orinHeight/orinWidth);
          //现在尺寸
          //计算想要截取的大小
          var iCurrentWidth=orinWidth;
          var iCurrentheight=orinHeight;
          if(fProportion>=0.645){
              iCurrentWidth=parseInt(orinWidth*0.645);
              iCurrentheight=parseInt(iCurrentWidth*0.645);
          }else{
              var NextWidth=parseInt(orinHeight/0.645);
              iCurrentWidth=parseInt(NextWidth*0.645);
              iCurrentheight=parseInt(iCurrentWidth*0.645);
          }
          var left=parseInt(orinWidth/2)-parseInt(iCurrentWidth/2);
          var top=parseInt(orinHeight/2)-parseInt(iCurrentheight/2);
          var crop_canvas = document.createElement('canvas');
          crop_canvas.width = 288;
          crop_canvas.height = 220;
          //图片大小截取和压缩
          crop_canvas.getContext('2d').drawImage(oOriginImg.get(0), left, top, iCurrentWidth, iCurrentheight, 0, 0,288,220);
          //图片的最终信息
          var imgUrl=crop_canvas.toDataURL('image/jpeg');
          //获取当前的视点信息
          var oMarkPos=camera.position;
          var oMarkDir=camera.direction;
          var oMarkUp=camera.up;
      }
    

素材预览

  • 实景三维数据的预览

  • 加载实景三维数据

     var sTimeSeconds=new Date().getTime();
     var tileset = new LSGlobe.LSPageLOD({
         //dataGuid 数据的唯一标识
         url :"http://localhost:8080/TX_DataManager/data/" + dataGuid + "/data/model.json?"+sTimeSeconds,
         shadows:LSGlobe.ShadowMode.ENABLED,
         // shadows属性值详解
         // LSGlobe.ShadowMode.ENABLED  即接受阴影也自己投影
         // LSGlobe.ShadowMode.DISABLED  即不接受投影也不自己投影
         // LSGlobe.ShadowMode.RECEIVE_ONLY 只接受阴影不自己投影
         // LSGlobe.ShadowMode.CAST_ONLY  不接受阴影只自己投影
         viewer:viewer
     });
     tileset.name={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
     //倾斜模型对象加入到球中
     viewer.scene.pageLODLayers.add(tileset);
     tileset.readyPromise.then(function(pagelod) {
         //飞到倾斜模型的位置(可解决中心点不准问题)
         var oCenter=tileset.tileBoundingSphere.center;
         var cartesian3=new LSGlobe.Cartesian3(oCenter.x,oCenter.y,oCenter.z);
         var cartographic=ellipsoid.cartesianToCartographic(cartesian3);
         var lat=LSGlobe.Math.toDegrees(cartographic.latitude);
         var lng=LSGlobe.Math.toDegrees(cartographic.longitude);
         var height=cartographic.height;
         viewer.camera.flyTo({
             destination: LSGlobe.Cartesian3.fromDegrees(lng, lat, height + 1500),
             duration: 1
         })
     }).otherwise(function(error){
         console.error("加载倾斜数据错误");
     });
    
  • 人工三维数据的预览

  • lmz加载(obj,fbx,skp都可转换为lmz)

     //人工三维加载的位置信息可自己设置
     var position={x:120,y:30,z:0};
     var model = new LSGlobe.LSModelLOD({
         //dataGuid 数据的唯一标识
         url :"http://localhost:8080/TX_DataManager/data/"+dataGuid+"/slf.lmz",
         shadows : LSGlobe.ShadowMode.ENABLED,
         position : new LSGlobe.Cartesian3(position.x,position.y, position.z)
         //position : new LSGlobe.Cartesian3("120","30","0")
     });
     model.name={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
     //人工模型加入到球中
     viewer.scene.primitives.add(model);
     //飞到人工模型的位置
     viewer.camera.flyTo({
         destination : LSGlobe.Cartesian3.fromDegrees(position.x,position.y,position.z+120),
         orientation : {
             heading : LSGlobe.Math.toRadians(0.0),
             pitch : LSGlobe.Math.toRadians(-90.0),
             roll : 0.0
         },
         duration: 2
     });
    
  • 3dtiles(skp可转换为3dtiles)

    
     var tileset = new LSGlobe.Cesium3DTileset({
         //dataGuid 数据的唯一标识
         url: "http://localhost:8080/TX_DataManager/data/"+dataGuid+"/Data/data.json",
         maximumScreenSpaceError:0
     });
     //上面的Cesium3DTileset如果不能正常加载可替换为LSGlobe3DTiles
     //3dtiles加入到球中
     tileset.name={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
     viewer.scene.primitives.add(tileset);
     tileset.readyPromise.then(function(tileset) {
         //飞到3dtiles模型的位置
         var boundingSphere = tileset.boundingSphere;
         viewer.camera.viewBoundingSphere(boundingSphere, new LSGlobe.HeadingPitchRange(0, -2.0, 0));
         viewer.camera.lookAtTransform(LSGlobe.Matrix4.IDENTITY);
     });
    
  • 矢量数据的预览

  • shp加载

     var options = {
         camera: viewer.scene.camera,
         canvas: viewer.scene.canvas,
         clampToGround: true
     };
     //dataGuid 数据的唯一标识
     var promise= LSGlobe.KmlDataSource.load("http://localhost:8080/TX_DataManager/data/"+dataGuid+"//data.geojson",options);
     promise.then(function(dataSource){
         dataSource.name ={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
         //shp加载到球中
         viewer.dataSources.add(dataSource);
         //飞到shp数据的位置
         viewer.flyTo(dataSource);
     }).otherwise(function(error) {
         console.error("加载shp数据失败");
     });
     矢量数据中的要素属性/属性值的获取,参考单体化鼠标点击获取
    
  • KML加载

     var options = {
         camera: viewer.scene.camera,
         canvas: viewer.scene.canvas,
         clampToGround: true
     };
     //dataGuid 数据的唯一标识
     var promise= LSGlobe.KmlDataSource.load("http://localhost:8080/TX_DataManager/data/"+dataGuid+"/data.kml",options);
     promise.then(function(dataSource){
         dataSource.name ={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
         //KML加载到球中
         viewer.dataSources.add(dataSource);
         //飞到KML数据的位置
         viewer.flyTo(dataSource);
     }).otherwise(function(error) {
         console.error("加载kml数据失败");
     });
    
  • 影像数据的预览

      var imageryLayers = viewer.imageryLayers;
      var imageLayer = new LSGlobe.LRPImageryProvider({
          "url" : "http://localhost:8080/api/lsglobe/1.0.0/",
          "layer" : "lrpdata/"+dataGuid+"/data.lrp"
          //dataGuid 数据的唯一标识
      })
      imageLayer.name ={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
      //影像加载到球中
      imageryLayers.addImageryProvider(imageLayer);
      imageLayer.readyPromise.then(function(flag) {
          var rect = imageLayer._rectangle;
          //飞到影像数据的位置
          viewer.camera.flyTo({
              destination : rect
          });
      }).otherwise(function(error){
          console.error("加载影像数据失败");
      })
    
  • 地形数据的预览

      var terrainLayers = viewer.terrainLayers;
      var terrainLayer = new LSGlobe.LSLRPTerrainProvider({
          "url" : "http://localhost:8080/api/lsglobe/1.0.0/",
          "layer" : "lrpdata/"+dataGuid+"/data.lrp"
          //dataGuid 数据的唯一标识
      })
      terrainLayer.name ={"在加入到场景时添加一些字段来区分各种数据,预览时无需添加"}
      //地形加载到球中
      terrainLayers.addTerrainProvider(terrainLayer);
      terrainLayer.readyPromise.then(function(flag) {
          var rect = imageLayer._rectangle;
          //飞到地形数据的位置
          viewer.camera.flyTo({
              destination : rect
          });
      }).otherwise(function(error){
          console.error("加载地形数据失败");
      })
    

场景

场景构建

  • 场景添加

  场景是用数据构建成的,场景中可以添加多种数据(目前该平台支持的多种数据都可添加),每种数据也可添加多个,对于人工模型转换为lmz格式的只上传一次即可在场景中添加多个相同的数据,用户可以使用自己添加的数据来构建自己想要的各种场景。

  1. 场景添加

      进行场景构建之前需要先定义场景,由于场景的数据保存是以json的格式保存所以也可人为的拼接一个和场景相关的json数据和定义场景关联起来,以达到场景构建时就拥有某些数据(该方法主要用于添加场景默认的底图)

    添加场景的接口

    http://localhost:1000/api/scene/1.0.0/add

    场景数据存储json和场景关联

    http://localhost:1000/api/scene/1.0.0/upload

  2. 场景信息修改

    场景的缩略图获取和视点的缩略图获取参考“素材修改”中缩略图获取截取方法

    根据id获取公开场景信息接口

    http://localhost:1000/api/scene/1.0.0/getPublic

    根据id获取私有场景信息接口

    http://localhost:1000/api/scene/1.0.0/getPrivate

    场景信息修改接口

    http://localhost:1000/api/scene/1.0.0/set

    场景默认视角获取

    http://localhost:1000/api/viewpoint/1.0.0/get/{id}

    场景默认视角设置 http://localhost:1000/api/viewpoint/1.0.0/setSceneDefault

    场景缩略图设置

    http://localhost:1000/api/scene/1.0.0/setImg

    设置场景公开私有

    http://localhost:1000/api/scene/1.0.0/setPower

  3. 场景管理

    场景信息列表获取

    http://localhost:1000/api/scene/1.0.0/get

    场景删除

    http://localhost:1000/api/scene/1.0.0/del

  4. 场景飞行到默认位置

     //position direction up保存的视角信息
     viewer.camera.flyTo({
         destination :new LSGlobe.Cartesian3(position.x,position.y,position.z),
         orientation : {
             direction : new LSGlobe.Cartesian3(direction.x,direction.y,direction.z),
             up : new LSGlobe.Cartesian3(up.x,up.y,up.z)
         }
     });
    
    
  5. 场景构建

    场景构建,构建场景需要将数据添加到场景中,添加的方法产考“素材预览”中的模型加入球中的方法,数据添加完成以后即可进行场景的保存(场景保存方式是json文件)

    1).将构建好的场景转换为json格式的数据用于保存 场景转换为json数据的方法

     viewer.toJSON();
    

    2).场景json介绍

     {
         "asset":{
             "version":"1.0.0",
             "date":"2018-08-15T05:17:03.6659999999974389Z"
             //可用于保底层库版本信息和常最后一个修改时间
         },
         "scene":{
             "properties":{
                 "minimumDisableDepthTestDistance":0,
                 "shadows":false
             },
             "imageryLayers":[
             //imageryLayers保存所有影像数据的对象
                 {
                     "show":true,
                     "name":"http://localhost:8080/LSGlobe/Build/LSGlobe/Assets/Textures/GlobalBkLayer.jpg",
                     "url":"http://localhost:8080/LSGlobe/Build/LSGlobe/Assets/Textures/GlobalBkLayer.jpg",
                     "tileWidth":1024,
                     "tileHeight":512,
                     "tilingScheme":{
                         "type":"GeographicTilingScheme",
                         "numberOfLevelZeroTilesX":1,
                         "numberOfLevelZeroTilesY":1
                     }
                     //球的底图示例(必须有不然只有一个蓝色的球)
                 },{
                     "type":"UrlTemplateImageryProvider",
                     "show":true,
                     "name":"google",
                     "url":"http://mt0.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}",
                     "tileWidth":256,
                     "tileHeight":256,
                     "maximumLevel":20,
                     "minimumLevel":0,
                     "tilingScheme":{
                         "type":"WebMercatorTilingScheme",
                         "numberOfLevelZeroTilesX":1,
                         "numberOfLevelZeroTilesY":1
                     }
                     //google影像格式数据示例
                 },{
                     "type":"LSLRCImageryProvider",
                     "show":true,
                     "name":"skyLandImage",
                     "url":"http://localhost:8080/lrc/skyLandMarker.lrc",
                     "tilingScheme":{
                         "type":"GeographicTilingScheme",
                         "numberOfLevelZeroTilesX":2,
                         "numberOfLevelZeroTilesY":1
                     }
                     //lrc影像格式数据示例
                 },{
                     "type":"LRPImageryProvider",
                     "show":true,
                     "name":{
                         "guid":"eb20018a-171e-46bd-8ddb-35fe3fe25f91",
                         "serverType":4,
                         "jsonPath":"",
                         "title":"suzhou",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"lrp",
                     },
                     "url":"http://10.16.30.12:8080/TX_DataManager",
                     "lrpName":"lrpdata/eb20018a-171e-46bd-8ddb-35fe3fe25f91/data.lrp",
                     "tileWidth":"256",
                     "tileHeight":"256",
                     "maximumLevel":"17",
                     "minimumLevel":"1",
                     "tilingScheme":{
                         "type":"GeographicTilingScheme",
                         "numberOfLevelZeroTilesX":2,
                         "numberOfLevelZeroTilesY":1
                     }
                     //lrp影像格式数据示例
                 }
             ],
             "terrainLayers":[
             //terrainLayers保存所有地形数据的对象
                 {
                     "type":"LRPTerrainProvider",
                     "show":true,
                     "name":{
                         "guid":"e4f05ecf-9133-ef41-ea45-e0b4bc763f85",
                         "serverType":5,
                         "jsonPath":"",
                         "title":"dixing-lrp",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"lrp"
                     },
                     "url":"http://localhost:8080/TX_DataManager",
                     "lrpName":"lrpdata/e4f05ecf-9133-ef41-ea45-e0b4bc763f85/data.lrp"
                     //lrp地形格式数据示例
                 }
             ],
             "layers":[
             //layers保存所有人工模型数据的对象
                 {
                     "type":"modelLOD",
                     "name":{
                         "guid":"f8adcd2a-5133-ef43-c892-2b2b512667f7",
                         "serverType":2,
                         "jsonPath":"",
                         "title":"fbx-small",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"fbx"
                     },
                     "url":"http://localhost:8080/TX_DataManager/data/ffec62ef-daed-40fe-a047-4b97ca705ce5/slf.lmz",
                     "position":{
                         "x":109.99999999999986,
                         "y":35.0037953410724,
                         "z":10
                     },
                     "rotate":{
                         "x":0,
                         "y":0,
                         "z":0
                     },
                     "scale":{
                         "x":"1",
                         "y":"1",
                         "z":"1"
                     },
                     "shadows":1,
                     "show":true
                     //lmz格式数据示例
                 },{
                     "name":{
                         "guid":"c5ea3f5b-8321-479c-ae8b-210523f68c67",
                         "serverType":2,
                         "jsonPath":"c5ea3f5b-8321-479c-ae8b-210523f68c67/Data/data.json",
                         "title":"leshou",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"skp"
                     },
                     "type":"Cesium3DTileset",
                     "url":"http://localhost:8080/TX_DataManager/data/c5ea3f5b-8321-479c-ae8b-210523f68c67/Data/data.json",
                     "maximumScreenSpaceError":0,
                     "shadows":1,
                     "show":true
                     //3DTiles格式数据示例
                 }
             ],
             "pageLODLayers":[
             //pageLODLayers保存所有实景三维数据的对象
                 {
                     "name":{
                         "guid":"d2a27e43-8133-ef41-ea44-a4d69ea8cada",
                         "serverType":1,
                         "jsonPath":"",
                         "title":"data-xiao",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"osgb"
                     },
                     "type":"pageLOD",
                     "url":"http://localhost:8080/TX_DataManager/data/d2a27e43-8133-ef41-ea44-a4d69ea8cada/data/model.json?1534310091372",
                     "shadows":1,
                     "show":true
                     //osgb格式数据示例
                 }
             ],
             "datasources":[
             //datasources保存所有矢量数据的对象
                 {
                     "name":{
                         "guid":"ce05ffa8-e133-ef41-ea44-5c8a13bd284c",
                         "serverType":3,
                         "jsonPath":"",
                         "title":"company_bouns",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"kml"
                     },
                     "show":true,
                     "type":"kmlDataSource",
                     "sourceUri":"http://localhost:8080/TX_DataManager/data/ce05ffa8-e133-ef41-ea44-5c8a13bd284c/data.kml",
                     "clampToGround":false
                     //kml格式数据示例
                 },{
                     "name":{
                         "guid":"a71da44b-3e5f-40f8-b2e5-c94604b95b65",
                         "serverType":3,
                         "jsonPath":"",
                         "title":"shptest",
                         "position":"120,30,0",
                         "description":"",
                         "dataType":"shp",
                     },
                     "show":true,
                     "type":"GeoJsonDataSource",
                     "sourceUri":"http://10.16.30.12:8080/TX_DataManager/data/a71da44b-3e5f-40f8-b2e5-c94604b95b65/data.geojson",
                     "attachPolygon":true,
                     "clampToGround":true
                     //shp格式数据示例
                 }
             ]
         }
     }
     //json数据中的name值是数据对应的基本新信息不可或缺,如果需要自定义添加字段也可以添加到name属性中
     name属性值的意义
     guid         //数据的唯一标识
     serverType   //数据的类型实景为1;人工为2;矢量为3;影像为4;地形为5
     jsonPath     //空值为lmz格式人模型工,有值为3dtiles格式人工模型
     title        //数据的名称
     position     //数据的位置
     description  //数据描述
     dataType     //源数据类型
    

    3).json保存接口

    http://localhost:1000/api/scene/1.0.0/upload

    4).json数据保存的数据信息不可改变,为防止更换ip或网站以后json数据不能正常读取,可将数地址网站或ip部分在保存时替换为某个特殊字符串,前台添加时再统一替换回当前的ip或网址比如将http://10.16.30.12:8080/替换为[rootPath]

    5).前台加载json文件

    加载场景接口

    http://localhost:1000/api/scene/1.0.0/getSceneJson

     //result.data
     上面借口返回的内容
     viewer.fromJSON(result.data);
     //使用上面方法数据会自动加载到球上形成一个场景
    

    6.如果json中的路径替换为“[rootPath]”保存后再次加载前,前台对json处理,再替换回来当前的ip或网址,函数如下

     function fnChangeResult(result) {
         result.scene.datasources = fnChangeSceneJson(result.scene.datasources, "datasources");
         result.scene.imageryLayers = fnChangeSceneJson(result.scene.imageryLayers, "imageryLayers");
         result.scene.layers = fnChangeSceneJson(result.scene.layers, "layers");
         result.scene.pageLODLayers = fnChangeSceneJson(result.scene.pageLODLayers, "pageLODlayers");
         result.scene.terrainLayers = fnChangeSceneJson(result.scene.terrainLayers, "terrainLayers");
         return result;
     }
     function fnChangeSceneJson(oSceneData, type) {
         for (var i = 0; i < oSceneData.length; i++) {
             if (type == "datasources") {
                 if(oSceneData[i].sourceUri){
                     oSceneData[i].sourceUri = oSceneData[i].sourceUri.replace("[ROOTPATH]", "");
                 }
                 if (oSceneData[i].name && typeof(oSceneData[i].name) == "string") {
                     oSceneData[i].name = oSceneData[i].name.replace("[ROOTPATH]", "当前的ip或网址");
                 }
             } else if (type == "imageryLayers") {
                 if (i == 0) {
                     oSceneData[i].name = oSceneData[i].name.replace("[ROOTPATH]","当前的ip或网址");
                 }
                 if(oSceneData[i].imageUrlTemplate){
                     oSceneData[i].imageUrlTemplate = oSceneData[i].imageUrlTemplate.replace("[ROOTPATH]", "当前的ip或网址");
                 }
                 oSceneData[i].url = oSceneData[i].url.replace("[ROOTPATH]", "当前的ip或网址");
             } else if (type == "layers") {
                 oSceneData[i].url = oSceneData[i].url.replace("[ROOTPATH]", "当前的ip或网址");
             } else if (type == "pageLODlayers") {
                 oSceneData[i].url = oSceneData[i].url.replace("[ROOTPATH]","当前的ip或网址");
             } else if (type == "terrainLayers") {
                 oSceneData[i].url = oSceneData[i].url.replace("[ROOTPATH]","当前的ip或网址");
                 if(oSceneData[i].tileUrlTemplate){
                     oSceneData[i].tileUrlTemplate=oSceneData[i].tileUrlTemplate.replace("[ROOTPATH]", "当前的ip或网址");
                 }
                 if(oSceneData[i].metadataUrl){
                     oSceneData[i].metadataUrl=oSceneData[i].metadataUrl.replace("[ROOTPATH]", "当前的ip或网址");
                 }
             }
         }
         return oSceneData;
     }
    

    7.替换完成后再执行viewer.fromJSON(result)将数据加载到球上,result处理后的json数据

    8.球的加载需要获取许可的信息 具体的获取接口

    http://localhost:1000/api/license/1.0.0/getLicenseConf

    返回信息是许可路径和许可码的拼接字符串用#分个开 许可路径和许可码用与初始化球

    9.初始化球的代码

     viewer = new LSGlobe.Viewer('cesiumContainer', {
         baseLayerPicker: false,
         sceneModePicker: false,
         fullscreenButton:false,
         guid:"许可码",//许可接口获取内容#前面部分
         "licenseUrl":"许可路径"//许可接口获取内容#后面部分
     });
    
  6. 场景中数据管理

      场景中一般有多个,多种类型的数据,我们需要对这些数据进行操作管理达到构建场景的目的。

    1.场景中的数据获取

     1).实景三维数据获取
         viewer.scene.pageLODLayers._pageLODs[1] //1是实景三维数据的索引
         //为了精确获取实景三维数据可使用根据数据id获取的方式获取,获取方法如下
         function getPageLODLayersById(primitiveid) {
             var aPrimitives = viewer.scene.pageLODLayers;
             var oPrimitive="";
             for (var i = 0; i < aPrimitives._pageLODs.length; i++) {
                 if (!!aPrimitives._pageLODs[i].name) {
                     if (aPrimitives._pageLODs[i].name.guid == primitiveid) {
                         oPrimitive = aPrimitives._pageLODs[i];
                         break;
                     }
                 }
             }
             return oPrimitive;
         }
     2).人工三维数据获取
         viewer.scene.primitives._primitives[1] //1是人工三维数据的索引
         //精确获取参照实景三维数据获取方法
     3).矢量数据获取
         viewer.dataSources._dataSources[1] //1是矢量数据的索引
         //精确获取参照实景三维数据获取方法
     4).影像数据获取
         viewer.scene.imageryLayers._layers[1] //1影像数据的索引
         //精确获取参照实景三维数据获取方法
     5).地形数据获取
         viewer.scene.terrainLayers._layers[1] //1是地形数据的索引
         //精确获取参照实景三维数据获取方法
    

    2.场景中的数据飞行

     1).实景三维数据飞行
         //tileset第一步获取的数据对象
         var oCenter=tileset.tileBoundingSphere.center;
         var cartesian3=new LSGlobe.Cartesian3(oCenter.x,oCenter.y,oCenter.z);
         var cartographic=ellipsoid.cartesianToCartographic(cartesian3);
    
         var lat=LSGlobe.Math.toDegrees(cartographic.latitude);
         var lng=LSGlobe.Math.toDegrees(cartographic.longitude);
         var height=cartographic.height;
         viewer.camera.flyTo({
             destination: LSGlobe.Cartesian3.fromDegrees(lng, lat, height + 1500),
             duration: 1
         })
     2).人工三维数据飞行
         //tileset第一步获取的数据对象(lmz格式)
         var addpointStrs = tileset._position.split(',');
         viewer.camera.flyTo({
             destination : LSGlobe.Cartesian3.fromDegrees(addpointStrs[0],addpointStrs[1], addpointStrs[2]+120),
             orientation : {
                     heading : LSGlobe.Math.toRadians(0.0),
                     pitch : LSGlobe.Math.toRadians(-90.0),
                     roll : 0.0
             },
             duration: 2
         });
         //tileset第一步获取的数据对象(3tiles格式)
         var boundingSphere = tileset.boundingSphere;
         viewer.camera.viewBoundingSphere(boundingSphere, new LSGlobe.HeadingPitchRange(0, -2.0, 0));
         viewer.camera.lookAtTransform(LSGlobe.Matrix4.IDENTITY);
     3).矢量数据飞行
         //datasource第一步获取的数据对象
         viewer.flyTo(datasource)
     4).影像数据飞行
             //imageLayer第一步获取的数据对象
             var rect = imageLayer._imageryProvider.rectangle;
             viewer.camera.flyTo({
                 destination: rect
             }, {duration: 3});
    
     5).地形数据飞行
             //terrainLayer第一步获取的数据对象
             var rect = terrainLayer._imageryProvider.rectangle;
             viewer.camera.flyTo({
                 destination: rect
             }, {duration: 3});
    

    3.场景中的数据显示隐藏

     1).实景三维数据显示隐藏
         //tileset第一步获取的数据对象
         tilese.show=false;
         //false 隐藏 true 显示
     2).人工三维数据显示隐藏
         //参考实景三维数据显示隐藏
     3).矢量数据显示隐藏
         //参考实景三维数据显示隐藏
     4).影像数据显示隐藏
         //参考实景三维数据显示隐藏
     5).地形数据显示隐藏
         //参考实景三维数据显示隐藏
    

    4.场景中的数据删除

     1).实景三维数据删除
         //tileset第一步获取的数据对象
         viewer.scene.pageLODLayers.remove(tileset)
     2).人工三维数据删除
         //tileset第一步获取的数据对象
         viewer.scene.primitives.remove(tileset)
     3).矢量数据删除
         //dataSources第一步获取的数据对象
         viewer.dataSources.remove(dataSources)
     4).影像数据删除
         //imageLayer第一步获取的数据对象
         viewer.imageryLayers.remove(imageLayer)
     5).地形数据删除
         //terainLayer第一步获取的数据对象
         viewer.imageryLayers.remove(terainLayer)
    

场景底图

  • 场景地图加载

  • 加载在线影像

     1).google影像
         var google = new LSGlobe.UrlTemplateImageryProvider({
             url : 'http://mt0.google.cn/vt/lyrs=s&hl=zh-CN&x={x}&y={y}&z={z}',
             tilingScheme : new LSGlobe.WebMercatorTilingScheme(),
             maximumLevel : 20,
             name:"google"
         });
         viewer.imageryLayers.addImageryProvider(google);
     2).天地图
         var tianditu = new LSGlobe.WebMapTileServiceImageryProvider({
             url: 'http://t0.tianditu.com/img_c/wmts?service=WMTS&version=1.0.0&request=GetTile&tilematrix={TileMatrix}&layer=img&style={style}&tilerow={TileRow}&tilecol={TileCol}&tilematrixset={TileMatrixSet}&format=tiles',
             layer: 'img',
             style: 'default',
             format: 'tiles',
             tileMatrixSetID: 'c',
             subdomains: ['t0', 't1', 't2', 't3', 't4', 't5', 't6', 't7'],
             maximumLevel: 18,
             tilingScheme: new LSGlobe.GeographicTilingScheme(),
             tileMatrixLabels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19']
         });
         viewer.imageryLayers.addImageryProvider(tianditu);
     3).bingmap
         var bing = new LSGlobe.BingMapsImageryProvider({
             url: 'https://dev.virtualearth.net',
             key: 'Ak1xFbfMYLAAsWU7cZMRyvDHY13HFN1PESWP98WJLoK49OQDb8HSeUjBV3MpC5Yf',
             mapStyle: LSGlobe.BingMapsStyle.AERIAL
         });
         viewer.imageryLayers.addImageryProvider(bing);
    
  • 加载lrc文件作为底图

     var oLrcPath="lrc文件路径";
     var oLrc=new LSGlobe.LSLRCImageryProvider(oLrcPath);
     viewer.imageryLayers.addImageryProvider(oLrc);
    
  • 设置图层的层级

     初始添加影像时设置层级
     viewer.imageryLayers.addImageryProvider(bing,index);
     //index影像图层的层级number类型,对lrp也适用
     //设置某一个影像为最高层级
     viewer.imageryLayers.raiseToTop(imageLayer)
     imageLayer获取的影像对象
    
  • 设置在线google地形(google地形为国外数据加载较慢)

     var stkTerrainProviderMeshes = new LSGlobe.CesiumTerrainProvider({
         url: 'http://assets02.agi.com/stk-terrain/v1/tilesets/world/tiles',
         requestWaterMask: true,
         requestVertexNormals: true
     });
     viewer.terrainLayers.addTerrainProvider(terrainLayer);
    

飞行

  • 飞行路径构建
  • 添加一条飞行路径

    添加飞行路径的接口

    http://localhost:1000/api/flyline/1.0.0/add

  • 修改某条飞行路径

    修改数飞行路径的名称和视点id集合 视点id集合是按照顺序获取飞行路径对应视点的重要参数 该参数在某条飞行路径添加视点删除视点时都需要同步更新

    http://localhost:1000/api/flyline/1.0.0/update

  • 获取飞行路径

    根据飞行路径的GUID查询其对应的视点列表

    http://localhost:1000/api/flyline/1.0.0/getBySid/{sId}

    sId 飞行路径唯一标识

    根据场景GUID获取所有飞行路径

    http://localhost:1000/api/viewpoint/1.0.0/getByFlyLine/{flId}

    flId 飞行路径唯一

  • 删除某条飞行路径

    删除一条飞行路径接口

    http://localhost:1000/api/flyline/1.0.0/del

  • 飞行路径与视点绑定

  • 在飞行路径中的添加一个视点

    飞行路径和视点进行绑定接口

    http://localhost:1000/api/viewpoint/1.0.0/add

    同步添加视点的图片接口

    http://localhost:1000/api/viewpoint/1.0.0/upload

    截取canvas方形图片

     var oOriginImg = $("#visibleImg");
     var canvasWidth = $("canvas").width();
     var canvasHeight = $("canvas").height();
     var sImg = getRenderer();
     oOriginImg.off("load");
     oOriginImg.attr("src", sImg);
     oOriginImg.attr("style", "width:" + canvasWidth + "px;height:" + canvasHeight + "px;");
     oOriginImg.off("load").load(function () {
         //原始尺寸
         var orinWidth = oOriginImg.width();
         var orinHeight = oOriginImg.height();
         //计算比例
         var fProportion = parseFloat(orinHeight / orinWidth);
         //现在尺寸
         var iCurrentWidth = orinWidth;
         var iCurrentheight = orinHeight;
         if (fProportion >= 1) {
             iCurrentWidth = parseInt(orinWidth * 0.6);
             iCurrentheight = iCurrentWidth;
         } else {
             iCurrentWidth = parseInt(orinHeight * 0.6);
             iCurrentheight = iCurrentWidth;
         }
         var left = parseInt(orinWidth / 2) - parseInt(iCurrentWidth / 2);
         var top = parseInt(orinHeight / 2) - parseInt(iCurrentheight / 2);
         var crop_canvas = document.createElement('canvas');
         crop_canvas.width = 150;
         crop_canvas.height = 150;
         crop_canvas.getContext('2d').drawImage(oOriginImg.get(0), left, top, iCurrentWidth, iCurrentheight, 0, 0, 150, 150);
         var imgUrl = crop_canvas.toDataURL("image/jpeg");
     });
    
     注意:每添加一个飞行视点或者删除一个视点都需要更新一下对应该条飞行路径的视点集合
    
  • 修改飞行路径中的某个视点

    修改飞行路径单个视点位置的坐标

    http://localhost:1000/api/viewpoint/1.0.0/update

    修改飞行路径的单个视点位置信息(名称和时间间隔)

    http://localhost:1000/api/viewpoint/1.0.0/updateNameTime

  • 删除飞行路径中的某个视点

    删除单个视点

    http://host:8080/api/viewpoint/1.0.0/del

    注意:每添加一个飞行视点或者删除一个视点都需要更新一下对应该条飞行路径的视点集合

  • 飞行路径的预览

  • 初始化一条飞行路径

     var cameraTrackControls = new LSGlobe.LSCameraTrackControls(viewer.scene);
    
  • 向飞行路径中添加视点信息

     //向飞行路径中添加单个视点
     //pos,direction,up这些值是保存视点中的相机位置信息
     //timeDelta 保存视点中的timeInterval值
     cameraTrackControls.getAllKeys().push(
     {
         destination: new LSGlobe.Cartesian3(posx,posy,posz),
         orientation: {
             direction: new LSGlobe.Cartesian3(directionx,directiony,directionz),
             up: new LSGlobe.Cartesian3(upx,upy,upz)
         },
         time: timeDelta
     });
     //添加视点完成该飞行路径初始化完成
    
  • 飞行路径的操作

     //开始飞行路径的飞行或继续飞行路径的飞行
     cameraTrackControls.play();
    
     //停止飞行路径的飞行
     cameraTrackControls.pause();
    
     //清除飞行路径并停止飞行路径的飞行
     cameraTrackControls.clear();
    

标绘

标绘功能包含标绘点、标绘线、标绘面。这些标绘信息统一保存在一个drawDataSource对象中,保存时数据可转换为json文件,加载时获取到之前保存的json,可以统一一次将各种标绘数据加载到场景中并与保存前的显示效果一致。

  1. 标绘绘制

    为了更好的绘制体验,标绘线面的绘制可以应用现在封装好的线面绘制插件来绘制。而标注的绘制需要在事件句柄中点击绘制添加

    1.初始化标绘存储对象

     var drawDataSource;
     //标绘存储对象绘制的标绘要素(线或者面)会保存到该对象中
     var promise = viewer.dataSources.add(new LSGlobe.GeoJsonDataSource("drawDataSource"));
     promise.then(function(dataSource) {
         drawDataSource = dataSource;
     }).otherwise(function(error){
    
     });
    

    2.标绘绘制

    1).绘制标绘点

     //第一步新加事件句柄
     var handler = new LSGlobe.ScreenSpaceEventHandler(viewer.scene.canvas);
     handler.setInputAction(function (movement) {
         //获取的坐标上添加标绘点,具体的坐标获取参照坐标转换
     }, LSGlobe.ScreenSpaceEventType.LEFT_CLICK);
    
     //第二部将标注添加到球上
     //drawDataSource 保存标绘集合的对象,标绘初始化定义的
     var NewPoint=drawDataSource.add({
         //标注的坐标 x,y,z 经度纬度和高度的值
         position:new LSGlobe.Cartesian3(x,y,z),
         label:{
             text : name,
             //标注文字描述
             font :FontSize+"px "+FontName,
             //"32px Microsoft YaHei" 标注文字大小类型
             style : LSGlobe.LabelStyle.FILL_AND_OUTLINE,
             outlineWidth :6,
             translucencyByDistance:new LSGlobe.NearFarScalar(1.5e2, 1.0, 1.5e5, 0.0),
             horizontalOrigin:LSGlobe.HorizontalOrigin.LEFT,
             pixelOffset : new LSGlobe.Cartesian2( 15, -15 ),
             disableDepthTestDistance : 0,
             //标注的遮挡距离设置为100则视角与标注的距离大于100米时会有遮挡
             scale:0.5
         },
         billboard : {
             image :iconPath,
             //标注图标路径
             width : 64,
             height : 64,
             disableDepthTestDistance : 1000000000,
             //标注的遮挡距离设置为100则视角与标注的距离大于100米时会有遮挡
             scale:0.5,
             translucencyByDistance:new LSGlobe.NearFarScalar(1.5e2, 1.0, 1.5e5, 0.0),
         },
         id:id,
         //标注唯一标识
         show:true/false
         //标绘点对象的显示隐藏属性
     });
     NewPoint.properties.addProperty(des,description);
     //使用该方法加入单个自定义属性  des自定义属性名 description自定义属性值
     //NewPoint刚刚添加的标绘点对象
     NewPoint.properties=new LSGlobe.PropertyBag({
         des:description
     });
     //使用该方法加入多个自定义属性  des自定义属性名 description自定义属性值
     //NewPoint刚刚添加的标绘点对象
     //标绘点自定义属性中一般需要有标绘点的图片地址信息和相机位置信息 这些信息需要添加到自定义的属性中用于再次加载时使用
    

    缩略图信息上传

    http://localhost:1000/api/viewpoint/1.0.0/update

    获取标注视点的canvas图片参照飞行路径中方形截图的获取方法

    每一个生成的标注信息都必须至少有一个tab页,在添加完标注以后为该标注添加一个文字的tab页

    标注的tab页信息

    tab页分为4种文、图片、视频、全景

    添加一个tab页(图片,视频,全景) http://localhost:1000/api/marker/1.0.0/addTabMedia

    添加一个tab页(文本) http://localhost:1000/api/marker/1.0.0/addTabTxt

    修改一个tab页(图片,视频,全景) http://localhost:1000/api/marker/1.0.0/updateTabMedia

    修改一个tab页(文本) http://localhost:1000/api/marker/1.0.0/updateTabTxt

    删除一个tab页

    http://localhost:1000/api/marker/1.0.0/delTab

    添加tab图片页中的一张图片 http://localhost:1000/api/marker/1.0.0/uploadTabImg

    添加tab页中的一个视频文件 http://localhost:1000/api/server/1.0.0/uploadVideo

    根据标注id获取该标注的所有tab信息 http://localhost:1000/api/marker/1.0.0/getTab

    2).绘制标绘线

     viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event) {
         //绘制插件绘制完成后使用返回的点集合来绘制标绘要素
         //使用返回的点来绘制空间线(普通线)
         //drawDataSource保存线面要素的对象
         drawDataSource.entities.add({
             //特殊字段该字段会保存下来,可以是字符串也可以是个对象
             name: "标绘线",
             //线的唯一标识会存储下来,再次加载也不改变,用于查找
             id:sEntityGuid,
             polyline: {
                 //线点的集合
                 positions: event.position,
                 //线宽
                 width:2,
                 //线的材质
                 material:LSGlobe.Color.fromCssColorString("rgb(0,186,255)"),
                 //线被遮挡的部分以虚线来显示
                 depthFailMaterial: new LSGlobe.PolylineDashMaterialProperty({
                     color:LSGlobe.Color.fromCssColorString("rgba(0,186,255,0.5)")
                 })
             }
         });
    
         //使用返回的点来绘制空间线(虚线)
         drawDataSource.entities.add({
             name: '虚线',
             id:sEntityGuid,
             polyline: {
                 positions: event.positions,
                 width: 4,
                 //线的材质,生成虚线的主要属性
                 material: new LSGlobe.PolylineDashMaterialProperty({
                     color: LSGlobe.Color.CYAN
                 })
             }
         });
    
         //使用返回的点来绘制空间线(带箭头线)
         drawDataSource.entities.add({
             name: '带箭头线',
             id:sEntityGuid,
             polyline: {
                 positions: event.positions,
                 width: 30,
                 followSurface: false,
                 //线的材质,生成虚线的主要属性
                 material: new LSGlobe.PolylineArrowMaterialProperty(LSGlobe.Color.PURPLE)
             }
         });
    
         //使用返回的点来绘制空间线(边框线)
         drawDataSource.entities.add({
             name: '边框线',
             id:sEntityGuid,
             polyline: {
                 positions: event.positions,
                 width: 20.0,
                 //线的材质,生成虚线的主要属性
                 material: new LSGlobe.PolylineOutlineMaterialProperty({
                     color: LSGlobe.Color.ORANGE,
                     outlineWidth: 2,
                     outlineColor: LSGlobe.Color.BLACK
                 })
             }
         });
    
         //使用返回的点来绘制空间线(发光线)
         drawDataSource.entities.add({
             name: '发光线',
             id:sEntityGuid,
             polyline: {
                 positions: event.positions,
                 width: 10,
                 //线的材质,生成虚线的主要属性
                 material: new LSGlobe.PolylineGlowMaterialProperty({
                     glowPower: 0.2,
                     color: LSGlobe.Color.BLUE
                 })
             }
         });
    
         //使用返回的点来绘制特殊面
         //drawDataSource保存线面要素的对象
         drawDataSource.entities.add({
             //特殊字段该字段会保存下来,可以是字符串也可以是个对象
             name: "标绘线",
             //线的唯一标识会存储下来,再次加载也不改变,用于查找
             id:sEntityGuid,
             corridor: {
                 //线点的集合
                 positions: event.positions,
                 //线宽
                 width: 5.0/100,
                 cornerType: LSGlobe.CornerType.ROUNDED,
                 //线的材质
                 material: LSGlobe.Color.fromCssColorString("rgb(0,186,255)"),
                 outline: true,
                 //线的类型
                 //LSGlobe.ClassificationType.BOTH           即可贴地又可贴模型
                 //LSGlobe.ClassificationType.TERRAIN        仅贴地
                 //LSGlobe.ClassificationType.CESIUM_3D_TILE 仅贴模型
                 classificationType: LSGlobe.ClassificationType.BOTH
             }
         });
     });
    

    3).绘制标绘面

     viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event){
         //绘制插件绘制完成后使用返回的点集合来绘制标绘要素
         //使用返回的点来绘制空间面(普通面)
         //drawDataSource保存线面要素的对象
         drawDataSource.entities.add({
             //特殊字段该字段会保存下来,可以是字符串也可以是个对象
             name: '标绘面',
             //面的唯一标识会存储下来,再次加载也不改变,用于查找
             id:id,
             clampToGround : false,
             attachPolygon:true,
             polygon: {
                 hierarchy:{
                     //面的点集合
                     positions :  event.positions
                 },
                 //面的材质
                 material:LSGlobe.Color.fromCssColorString("rgb(0,186,255)"),
                 //是否填充面
                 fill: true,
                 //是否显示面的外边框
                 outline: true,
                 //面的外边框的材质
                 outlineColor: LSGlobe.Color.fromCssColorString("rgb(0,186,255)"),
                 //面的外边框的宽度
                 outlineWidth:2,
                 perPositionHeight:true
             }
         });
    
         //使用返回的点来绘制特殊面
         //drawDataSource保存线面要素的对象
         drawDataSource.entities.add({
             //特殊字段该字段会保存下来,可以是字符串也可以是个对象
             name:'标绘面',
             //面的唯一标识会存储下来,再次加载也不改变,用于查找
             id:id,
             polygon: {
                 hierarchy: {
                     //面的点集合
                     positions: event.positions,
                 },
                 //面的材质
                 material: LSGlobe.Color.fromCssColorString("rgb(0,186,255)"),
                 //是否填充面
                 fill: true,
                 //面的类型
                 //LSGlobe.ClassificationType.BOTH           即可贴地又可贴模型
                 //LSGlobe.ClassificationType.TERRAIN        仅贴地
                 //LSGlobe.ClassificationType.CESIUM_3D_TILE 仅贴模型
                 classificationType: LSGlobe.ClassificationType.BOTH
             }
         });
     });
    
  2. 标绘要素获取
     //id 绘制线面时加入的唯一标识
     var entities = drawDataSource.entities.getById(id)
    
  3. 标绘显示隐藏
     //entities获取到的标绘要素对象
     entities.show=true/false;
    
  4. 标绘飞行

     //entities获取到的标绘要素对象
    
     //空间线面的飞行(发光线等都是空间线)
     viewer.flyto(entities)
    
     //特殊线面的飞行(贴地,贴模型)
     //使用单体化的飞行方式飞行
    
    
  5. 标绘删除

    
     //单个标绘线面删除
     //id绘制时添加的唯一标识
     drawDataSource保存线面要素集合的对象
     drawDataSource.entities.removeById(id)
    
     //删除drawDataSource对象中的所有标绘要素
     drawDataSource.entities.removeAll();
    
    
  6. 标绘编辑
     //entities获取到的标绘要素对象
     1.编辑标绘点
     //markid是标注添加时的唯一标识
     entities.billboard =new LSGlobe.BillboardGraphics({ //图标
         image :iconPath,
         //标注图标
         width : 64,
         height : 64,
         pixelOffset : new LSGlobe.Cartesian2( 0, -13 ),  //偏移量
         disableDepthTestDistance :  0,
         scale:0.5,
         translucencyByDistance:new LSGlobe.NearFarScalar(1.5e2, 1.0, 1.5e5, 0.0),
     });
     //修改标注图标信息
     entities.label=new LSGlobe.LabelGraphics({
         text : objEntity.name,
         //修改后的文字信息
         font : (parseInt(objEntity.FontSize)*2)+"px "+objEntity.FontName,
         //"32px Microsoft YaHei" 修改后的文字类型和大小
         fillColor:LSGlobe.Color.fromCssColorString('rgb(0,0,0)'),
         //文字填充颜色
         style : LSGlobe.LabelStyle.FILL_AND_OUTLINE,
         outlineWidth :6,
         translucencyByDistance:new LSGlobe.NearFarScalar(1.5e2, 1.0, 1.5e5, 0.0),
         horizontalOrigin:LSGlobe.HorizontalOrigin.LEFT,
         pixelOffset : new LSGlobe.Cartesian2( 15, -15 ),
         disableDepthTestDistance :  0,
         scale:0.5
     });
     //修改标注文字信息
     entities.position=new LSGlobe.Cartesian3.fromDegrees(position.x,position.y,position.z);
     //修改标注位置信息
     entities.label.text = new LSGlobe.ConstantProperty(objEntity.name);
     //单独修改标注文字信息中的文本
     entities.label.fillColor=LSGlobe.Color.fromCssColorString('rgb(255,255,255)');
     //单独修改标注文字信息中的文本填充颜色
     2.编辑标绘线
     1).编辑空间线的属性
         //编辑线的特殊字段
         entities.name = '面标绘名称';
         //编辑线宽度
         entities.polyline.width=10;
         //编辑线材质(颜色)
         entities.polyline.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)');
         //编辑线的透明度
         entities.polyline.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5')
         //编辑线的遮挡部分虚线属性
         entities.polyline.depthFailMaterial = new LSGlobe.PolylineDashMaterialProperty({
             color: LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5')
         });
     2).编辑特殊线的属性
         //编辑线的特殊字段
         entities.name= '面标绘名称';
         //编辑线宽度
         entities._corridor.width=10;
         //编辑线材质(颜色)
         entities._corridor.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)');
         //编辑线的透明度
         entities._corridor.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5');
     3.编辑标绘线
         //编辑面的特殊字段
         entities.name= '面标绘名称';
         //编辑面的材质
         entities.polygon.material=LSGlobe.Color.fromCssColorString('rgb(0,0,0)');
         //编辑面的透明度
         entitie.polygon.material=LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5');
         //编辑面的外边框(特殊面不显示边框不能设置该属性)
         entitie.polygon.outline=true/false;
         //编辑面是否填充
         entitie.polygon.fill=true/false;
         //编辑面外边框材质(特殊面不显示边框不能设置该属性)
         entitie.polygon.outlineColor=LSGlobe.Color.fromCssColorString('rgb(0,0,0)');
         //编辑面外边框透明度(特殊面不显示边框不能设置该属性)
         entitie.polygon.outlineColor=LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5');
    
  7. 标绘存储

    标绘的存储是以json格式保存的

    1).将标绘对象转换为json数据

     drawDataSource.toGeoJson()
    

    2).转换为标绘对象的数据通过保存接口保存到后台 保存标绘接口 http://localhost:1000/api/attach/1.0.0/upload

  8. 标绘重载

    标绘获取接口

    http://localhost:1000/api/attach/1.0.0/getDraw

     //sceneid标绘对应场景的唯一标识
    
     var geojsonUrl = result.data;
     //result.data前面获取的标绘数据json
     var promise =LSGlobe.GeoJsonDataSource.load(geojsonUrl, {});
     promise.then(function(dataSource){
         //将数据再次加载到初始化存储标绘要素的对象中  
         drawDataSource = dataSource;
         //添加到球中
         viewer.dataSources.add(dataSource);
     }).otherwise(function(error) {
         console.log(error);
     });
    
     //注意:为了防止标绘对象的多次重复加载需要判断viewer.dataSources中是否有路径为geojsonUrl的对象,如果有直接将该对象赋值给drawDataSource即可
    

单体化

单体化的绘制需要用到绘制插件插件的应用参考标绘绘制时的插件应用

  1. 单体化绘制

     1).初始化单体化存储对象
     var attachPolygonDataSource;
     //单体化存储对象,绘制的单体化要素会保存到该对象中
     var promise = viewer.dataSources.add(new LSGlobe.GeoJsonDataSource("attachPolygonDataSource"));
    
     promise.then(function(dataSource) {
         attachPolygonDataSource= dataSource;
     }).otherwise(function(error){
    
     });
     2).单体化绘制
     viewer.geometryEditer.viewModel.defaultSurfaceOptions.classificationType = LSGlobe.ClassificationType.CESIUM_3D_TILE;
     //单体化绘制时必须是贴模型的的该设置不能去掉
     viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYGON);
     viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
     viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function(event){
         //根据返回的点来生成单体化面
         //添加单体化面前需要将返回点中的高度值去掉
         var _pointArray=[];
         for(var i=0;i<event.positions.length-2;i=i+3){
             _pointArray.push(oValuePoint[i]);
             _pointArray.push(oValuePoint[i+1]);
         }
         //绘制单体化
         attachPolygonDataSource.entities.add({
             name: '单体化面',
             //单体化的唯一标识
             id:id,
             attachPolygon:true,
             polygon: {
                 hierarchy: {
                     //单体化的点集合
                     positions : _pointArray
                 },
                 //单体化的材质
                 material:new LSGlobe.Color(0.5,1.0,1.0,0.7),
                 //单体化面的材质是否填充
                 fill: true,
                 outline: false,
                 outlineColor: LSGlobe.Color.YELLOW
             }
     });
     });
    
  2. 单体化要素获取
     //id 绘制单体化时加入的唯一标识
     var entities = attachPolygonDataSource.entities.getById(id)
    
  3. 单体化显示隐藏
     //entities获取到的单体化对象
     entities.show=true/false;
    
  4. 单体化飞行

    
     //entities获取到的单体化对象
     //单体化飞行(贴地,贴模型)
     var entity = attachPolygonDataSource.entities.getById(singleId);
     if(entity){
         entity.polygon.material = LSGlobe.Color.fromRandom({
             red:entity.polygon.material.color._value.red,
             green:entity.polygon.material.color._value.green,
             blue:entity.polygon.material.color._value.blue,
             alpha : 0.7
         });
         var entityPosition = getRealPosition(entity);
         //获取单体化真实飞行坐标
         viewer.camera.flyTo({
             destination: LSGlobe.Cartesian3.fromDegrees(entityPosition.lng,entityPosition.lat,entityPosition.alt),
             duration: 3
         })
     }
     function getRealPosition(Entities){
         if(Entities._corridor){
             var aPos=Entities._corridor.positions._value;
         }else{
             var aPos=Entities.polygon.hierarchy._value.positions||Entities.polygon.hierarchy._value;
         }
         var iX=0,iY=0,iZ=0,maxX=0,maxY=0,minX,minY,realheight;
         for(var i=0;i<aPos.length;i++){
             iX=aPos[i].x+iX;
             iY=aPos[i].y+iY;
             iZ=aPos[i].z+iZ;
         }
         iX=iX/aPos.length;
         iY=iY/aPos.length;
         iZ=iZ/aPos.length;
         //中心点经纬度
         var WorlsPos=new LSGlobe.Cartesian3(iX,iY,iZ);
         var oDegree=ellipsoid.cartesianToCartographic(WorlsPos);
         var lng=LSGlobe.Math.toDegrees(oDegree.longitude);
         var lat=LSGlobe.Math.toDegrees(oDegree.latitude);
         for(var a=0;a<aPos.length;a++){
             //当前点的经纬度
             var sWorlsPos=new LSGlobe.Cartesian3(aPos[a].x,aPos[a].y,aPos[a].z);
             var sDegree=ellipsoid.cartesianToCartographic(sWorlsPos);
             var slng=LSGlobe.Math.toDegrees(sDegree.longitude);
             var slat=LSGlobe.Math.toDegrees(sDegree.latitude);
             //中心点经纬度
             var oPos = {"x":slng,"y":slat};
             var  currentheight = getDistance(oPos,lng,lat);
             if(a==0){
                 realheight=currentheight
             }
             if(realheight<currentheight){
                 realheight=currentheight
             }
         }
         var sTileset=whichTileset(lng,lat)[0];
         //获取单体化当前依附的模型
         if(sTileset){
             var alt=realheight*1.7320508075689+50 + sTileset._boundingSphere.center.z*1+sTileset.name.position.split(",")[2]*1;
             return {"lat":lat,"lng":lng,"alt":alt};
         }else{
             return {"lat":lat,"lng":lng,"alt":realheight};
         }
         return {"lat":lat,"lng":lng,"alt":alt};
     }
     //根据经纬度获取当前经纬度下的模型集合
     //x经度值 y维度值
     function whichTileset(x,y){
         var returnValue=[];
         for(var i=0;i<viewer.scene.pageLODLayers._pageLODs.length;i++) {
             var aTileSet = viewer.scene.pageLODLayers._pageLODs[i];
             var oCenter = aTileSet.tileBoundingSphere.center;
             var ellipsoid = viewer.scene.globe.ellipsoid;
             var cartesian3 = new LSGlobe.Cartesian3(oCenter.x, oCenter.y, oCenter.z);
             var cartographic = ellipsoid.cartesianToCartographic(cartesian3);
             var lat = LSGlobe.Math.toDegrees(cartographic.latitude);
             var lng = LSGlobe.Math.toDegrees(cartographic.longitude);
             var position = {x: lng, y: lat, z: cartographic.height};
             var distance = getDistance(position, x, y);
             if (distance < aTileSet.tileBoundingSphere.radius) {
                 returnValue.push(aTileSet);
             }
         }
         return returnValue;
     }
     获取两个经纬度之间的距离函数
     function getDistance(position,x,y){
         var radLatA = position.y*0.0174532925199432957692369077;
         var radLatB = y*0.0174532925199432957692369077;
         var radLonA = position.x*0.0174532925199432957692369077;
         var radLonB = x*0.0174532925199432957692369077;
         return Math.acos(
                         Math.cos(radLatA)*Math.cos(radLatB)*Math.cos(radLonA-radLonB)+
                         Math.sin(radLatA)*Math.sin(radLatB)) * 6378137;
     }
    
  5. 单体化删除

    
     //单个单体化删除
     //id绘制时添加的唯一标识
     attachPolygonDataSource保存单体化集合的对象
     attachPolygonDataSource.entities.removeById(id)
    
     //删除attachPolygonDataSource对象中的所有单体化
     attachPolygonDataSource.entities.removeAll();
    
    
    1. 单体化编辑

      //entities获取到的单体化要素对象
      
      1.编辑单体化
       //编辑单体化的特殊字段
       entities.name = '单体化面';
       //编辑单体化材质(颜色)
       entities.polygon.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)');
       //编辑单体化的透明度
       entities.polygon.materia= LSGlobe.Color.fromCssColorString('rgb(0,0,0)').withAlpha('0.5')
      
      2.编辑单体化的属性属性值
       1).获取单体化的属性属性值
       entities.properties
       //单体化所有的属性和属性值(自定义的属性属性值和本身具有的属性值)都保存在该对象里
       //获取属性和属性值展示出来
       var entityProperties = {};
       for (var index = 0; index < entities.properties.propertyNames.length;index++){
           var propertie = entities.properties.propertyNames[index];
           var value = entities.properties[propertie]._value;
           entityProperties[propertie] = value;
           //当前单体化所有属性的集合
           var isSelfValue=["marker-color","marker-size","title","fill","fill-opacity","stroke-opacity","stroke","stroke-width"];
           //单体化自带属性集合,在属性展示的时候不需要展示出来
           if(isSelfValue.indexOf(propertie)==-1){
               //只展示自定义的属性和属性值
           }
       }
       2).设置单体化的属性属性值
       //添加属性属性值
       entityProperties["属性"]="属性值";
       entityProperties当前单体化所有属性的集合;
       entities.properties = entityProperties;
       //删除属性属性值
       delete entityProperties["属性"];
       entityProperties当前单体化所有属性的集合;
       entities.properties = entityProperties;
       3).单体化属性值得展示
       //将第一步中获取的entityProperties中的属性名/属性值展示出来
      
  6. 单体化(以及单体化类似对象)的鼠标点击获取
     //初始化一个鼠标事件
     var handlerMonomer = new LSGlobe.ScreenSpaceEventHandler(viewer.scene.canvas);
     handlerMonomer.setInputAction(function (movement) {
         var posit = scene.pick(movement.startPosition);
         if (LSGlobe.defined(posit)){
             //如果posit值存在说明鼠标点击的地方有内容
             if(!!posit.id){
                 //如果posit.id存在,那么其就是当前选中的内容
                 //(该内容可以是标注,标绘,单体化,矢量数据中的要素)需要判断该数据是什么类型需要根据该对象中的特殊字段来判断
             }
         }
     }, LSGlobe.ScreenSpaceEventType.LEFT_CLICK);
    
  7. 单体化的存储

    单体化的存储是以json格式保存的

    1).将单体化对象转换为json数据

     attachPolygonDataSource.toGeoJson()
    

    2).转换为单体化对象的数据通过保存接口保存到后台 保存单体化接口 http://localhost:1000/api/attach/1.0.0/upload

  8. 单体化重载

    单体化加载获取接口

    http://localhost:1000/api/attach/1.0.0/getAttach

     var geojsonUrl = result.data;
     //result.data上面借口获取的数据
     var promise =LSGlobe.GeoJsonDataSource.load(geojsonUrl, {});
     promise.then(function(dataSource){
         //将数据再次加载到初始化存储单体化要素的对象中  
         attachPolygonDataSource = dataSource;
         //添加到球中
         viewer.dataSources.add(dataSource);
     }).otherwise(function(error) {
         console.log(error);
     });
    
     注意:为了防止单体化对象的多次重复加载需要判断viewer.dataSources中是否有路径为geojsonUrl的对象,如果有直接将该对象赋值给attachPolygonDataSource即可
    

双屏对比

  1. 添加双屏对比

    将其他的场景和该场景关联起来

    http://localhost:1000/api/contrasts/1.0.0/add

  2. 双屏对比管理

    删除与本场景关联的某个场景

    http://localhost:1000/api/contrasts/1.0.0/del

    查询与本场景关联的所有场景

    http://localhost:1000/api/contrasts/1.0.0/get

  3. 双屏对比联动

     双屏对比的原理:是同一个页面引用两个相同的iframe页面,每个页面加载一个预览的场景;想要双屏联动,必须在一个iframe进行操作时,另一个iframe中球的画面实时的跟着移动,这样就需要两个球的相机位置相同
    
     在一个iframe 中获取到另一个iframe的document对象
     var otherIframe = parent.document.getElementById("另一个iframe的唯一标识 id").contentWindow
    
     //camera.position,camera.direction,camera.up
     //当前iframe中球的相机位置
     otherIframe.jumpToViewPoint(camera.position,camera.direction,camera.up);
     //为了让两个球的相机位置相同,移动一个iframe中球的相机位置时,让另一个球的相机位置同步飞到当前iframe中球的相同的相机位置即可
     function jumpToViewPoint(position,direction,up){
         viewer.camera.flyTo({
             destination :new LSGlobe.Cartesian3(position.x,position.y,position.z),
             orientation : {
                 direction : new LSGlobe.Cartesian3(direction.x,direction.y,direction.z),
                 up : new LSGlobe.Cartesian3(up.x,up.y,up.z)
             },
             duration:0
         });
     }
     //上面的操作需要放到鼠标滑动事件中
     var handler = new LSGlobe.ScreenSpaceEventHandler(viewer.scene.canvas);
     handler.setInputAction(function (movement) {
         //在此处执行双屏对比的联动事件
     }, LSGlobe.ScreenSpaceEventType.WHEEL);
    

压平

压平面的保存和标绘单体化不同不能使用togeojson()的方式直接转换为json文件,所以压平的json文件是自己定义的

  1. 压平绘制

     1).先初始化一个文件保存压平面的数据(名称和点集合)
     var oFlatPosArray={};
    
     2).初始化一个保存压平面的对象
     //压平面无法飞行需要保存一个和压平面同步的空间面,用于飞行
     var pressurePolygonDataSource;
     //单体化存储对象,绘制的单体化要素会保存到该对象中
     var promise = viewer.dataSources.add(new LSGlobe.GeoJsonDataSource("pressurePolygonDataSource"));
     promise.then(function(dataSource) {
         pressurePolygonDataSource= dataSource;
     }).otherwise(function(error){
    
     });
    
     3).压平面的绘制
     viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYGON);
     viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
     viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event) {
         //使用绘制结束后的数据来进行压平面的绘制
         //返回的event.maxHeight和event.minHeight分别是绘制的所有点中高度的最高值和最低值
         var oHeight=((event.maxHeight*1+event.minHeight*1)/2).toFixed(2);
         该值作为压平面的初始高度来压平模型
         //返回坐标集合做变换
         var oValuePoint = ChangePolygonPosition(event.positions);
         //点集合设置为统一高度
         for (var i = 2; i < oValuePoint.length; i = i + 3) {
             oValuePoint[i] = oHeight;
         }
         //该面是辅助设置压平面的面,最后压平的高度和范围是该面显示的高度和范围
         pressurePolygonDataSource.entities.add({
             name: '压平面',
             id: id+"-common",
             polygon: {
                 hierarchy: LSGlobe.Cartesian3.fromDegreesArrayHeights(oValuePoint),
                 material: new LSGlobe.Color(0.5, 1.0, 1.0, 0.7),
                 fill: true,  //显示填充
                 outline: true,
                 outlineColor: LSGlobe.Color.YELLOW,
                 perPositionHeight: true
             }
         });
         oFlatPosArray[id]={};
         oFlatPosArray[id].name='压平面';
         oFlatPosArray[id].positions=oValuePoint;
         oFlatPosArray[id].show=true;
         //将压平面的信息保存到压平面json文件中
     })
     坐标转换函数
     function ChangePolygonPosition(oPositions) {
         var  oValuePoint = [];
         for(var a=0;a<oPositions.length;a++){
             var CurrentPoint = oPositions[a];
             var cartographic = LSGlobe.Cartographic.fromCartesian(CurrentPoint);
             var currentClickLon = LSGlobe.Math.toDegrees(cartographic.longitude);
             var currentClickLat = LSGlobe.Math.toDegrees(cartographic.latitude);
             oValuePoint.push(currentClickLon);
             oValuePoint.push(currentClickLat);
             oValuePoint.push(cartographic.height);
         }
         return oValuePoint;
     };
     4).模型的压平
    
     //id+"-common"为辅助压平面的唯一标识
     var  entity= pressurePolygonDataSource.entities.getById(id+"-common");
     var pos = entity.polygon.hierarchy._value.positions;
     if (!pos) {
         pos = entity.polygon.hierarchy._value;
     }
     //获取该压平面的辅助面中的坐标值
     var mtileset = undefined;
     //定义一个存放模型的变量
     for (var i = 0; i < pos.length; i++) {
         var cartographic = LSGlobe.Cartographic.fromCartesian(pos[i]);
         var longitude=LSGlobe.Math.toDegrees(cartographic.longitude);
         var latitude=LSGlobe.Math.toDegrees(cartographic.latitude);
         mtileset = whichTileset(longitude,latitude);
         if (mtileset.length < 1) {
             break;
         }
     }
     //根据辅助面中的点来获取需要压平的模型
    
     var myPolygon = new LSGlobe.PolygonGeometry({
         polygonHierarchy: new LSGlobe.PolygonHierarchy(pos),
         perPositionHeight: true
     });
     //使用辅助面的点集合绘制一个面对象
     for (var i = 0; i < mtileset.length; i++) {
         mtileset[i]._flattenPolygon.push(myPolygon);
         mtileset[i]._needUpateFlatten = true;
     }
     //将面对象添加到模型上进行压平,压平效果可以看到
     entity.show=false;
     //辅助面隐藏显示
    
     //获取当前压平的模型对象函数
     function whichTileset(x,y){
         var returnValue=[];
         for(var i=0;i<viewer.scene.pageLODLayers._pageLODs.length;i++) {
             var aTileSet = viewer.scene.pageLODLayers._pageLODs[i];
             var oCenter = aTileSet.tileBoundingSphere.center;
             var ellipsoid = viewer.scene.globe.ellipsoid;
             var cartesian3 = new LSGlobe.Cartesian3(oCenter.x, oCenter.y, oCenter.z);
             var cartographic = ellipsoid.cartesianToCartographic(cartesian3);
             //ellipsoid表示球对象
             var lat = LSGlobe.Math.toDegrees(cartographic.latitude);
             var lng = LSGlobe.Math.toDegrees(cartographic.longitude);
             var position = {x: lng, y: lat, z: cartographic.height};
             var distance = getDistance(position, x, y);
             if (distance < aTileSet.tileBoundingSphere.radius) {
                 returnValue.push(aTileSet);
             }
         }
         return returnValue;
     }
     //获取两个经纬度之间的距离函数
     function getDistance(position,x,y){
         var radLatA = position.y*0.0174532925199432957692369077;
         var radLatB = y*0.0174532925199432957692369077;
         var radLonA = position.x*0.0174532925199432957692369077;
         var radLonB = x*0.0174532925199432957692369077;
         return Math.acos(
                         Math.cos(radLatA)*Math.cos(radLatB)*Math.cos(radLonA-radLonB)+
                         Math.sin(radLatA)*Math.sin(radLatB)) * 6378137;
     }
    
  2. 压平面的修改

     1).设置高度
     var entity = pressurePolygonDataSource.entities.getById(id+"-common");
     //获取压平辅助面
     var aFlatArray = oFlatPosArray[id];
     for (var i = 2; i < aFlatArray.positions.length; i = i + 3) {
         aFlatArray[i] = height;
         //height 改变后的高度值
     }
     //此时oFlatPosArray中的position值已经发生了改变
     pressurePolygonDataSource.entities.remove(entity);
     //移除压平辅助面
     pressurePolygonDataSource.entities.add({
         name: "压平面",
         id: id+"-common",
         polygon: {
             hierarchy: LSGlobe.Cartesian3.fromDegreesArrayHeights(aFlatArray),
             material: new LSGlobe.Color(0.5, 1.0, 1.0, 0.7),
             fill: true,
             outline: true,
             outlineColor: LSGlobe.Color.YELLOW,
             perPositionHeight: true
         }
     });
     //重新添加压平辅助面
     //点击确定修改高度按钮
     // 清空所有压平面和辅助面
     var aPagelodLayers=viewer.scene.pageLODLayers._pageLODs;
     for (var i = 0; i < aPagelodLayers.length; i++) {
         try {
             aPagelodLayers[i].cleanflattenPolygon();
         }catch (e){
    
         }
     }
     pressurePolygonDataSource.entities.removeAll();
     //循环加载所有压平面和辅助面
     for (var i in oFlatPosArray) {
         var pos = oFlatPosArray[i].positions;
         var mtileset = undefined;
         mtileset = whichTileset(pos[0], pos[1]);
         //加载所有辅助面即使压平面隐藏也可飞到该位置
         pressurePolygonDataSource.entities.add({
             name: '压平面',
             id: i+"-common",
             clampToGround : true,
             attachPolygon : true,
             polygon: {
                 hierarchy: LSGlobe.Cartesian3.fromDegreesArrayHeights(pos),
                 material: new LSGlobe.Color(0.5, 1.0, 1.0, 0.7),
                 fill: true,  //显示填充
                 outline: false,
                 outlineColor: LSGlobe.Color.YELLOW
             }
         });
         var  entity= pressurePolygonDataSource.entities.getById(id+"-common").show=false;
         if (mtileset.length < 1 || pos.show==false) {
             // 如果是处于隐藏状态和面没有在模型上的压平面不再绘制
             break;
         }
         var myPolygon = new LSGlobe.PolygonGeometry({
             polygonHierarchy: new LSGlobe.PolygonHierarchy(LSGlobe.Cartesian3.fromDegreesArrayHeights(pos)),
             perPositionHeight: true
         });
         for (var j = 0; j < mtileset.length; j++) {
             mtileset[j]._flattenPolygon.push(myPolygon);
             mtileset[j]._needUpateFlatten = true;
         }
     }
    
     2).设置名称
     oFlatPosArray[id].name="修改后的名称";
    
    
  3. 压平面的飞行

     //压平面的飞行借助了压平辅助面
     var  entity= pressurePolygonDataSource.entities.getById(id+"-common");
    
     viewer.flyTo(entity);
    
  4. 设置显示隐藏
     //第一步更新数据的显示隐藏数据
     oFlatPosArray[id].show=false/true;
     //第二步删除所有压平面(参考压平高度修改)
     //第三步只加载oFlatPosArray[id].show=true的压平面
    
  5. 压平面的删除
     // 第一步删除数据中保存的压平面信息
     delete oFlatPosArray[id];
     //第二步删除所有压平面(参考压平高度修改)
     //第三步只加载oFlatPosArray[id].show=true的压平面
    
  6. 压平面的保存

    1).压平面的存储是以json格式保存的

    保存压平面数据的json集合oFlatPosArray通过保存接口保存到后台

    2)保存压平面时接口

    http://localhost:1000/api/attach/1.0.0/upload

  7. 压平面的重载

    压平接口获取

    http://localhost:1000/api/attach/1.0.0/getFlatten

     //将保存的压平面的信息保存重新复制
     oFlatPosArray = result.data;
     result.data 上面借口获取的内容
     //加载压平面 参考压平面的绘制(单个压平面加载)
    

模型裁剪

  • 模型裁剪绘制

      //使用绘制插件来绘制裁剪面
      viewer.geometryEditer.viewModel.defaultSurfaceOptions.classificationType =LSGlobe.ClassificationType.CESIUM_3D_TILE;
      viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYGON);
      viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
      viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event){
          //绘制完成以后执行事件 返回值event.position 绘制的所有点的集合
          var clipPolygon = new LSGlobe.PolygonGeometry({
              polygonHierarchy : new LSGlobe.PolygonHierarchy(event.positions),
              perPositionHeight : true
          });
          tileset.addClipPolygon(clipPolygon);
          //绘制范围内显示
          tileset.addPitPolygon(pitPolygon);
          //绘制范围内隐藏
      });
    
    
  • 模型保存重载

    模型裁剪暂时还没有后台接口无法保存修改,具体的方法可以参考压平的保存方式

分析

光照分析

//是否开启光照
viewer.shadows=true/false;

//光照阴影位置和当前的时间有关
//设置当前时间可以改变阴影的位置
var JulianDate =computes("当前时间");
//当天已经过去的时间单位s
var julianSecound=hour*60*60+minute*60+secound;
viewer.clock.currentTime= new LSGlobe.JulianDate(julianDay,julianSecound);

当前时间转换为朱莉安时间
function computes(now){
    var month = now.getUTCMonth() + 1;
    var day = now.getUTCDate();
    var year = now.getUTCFullYear()
    var y = eval(year);
    var m = eval(month);
    var d = eval(day);
    var extra = 100.0*y + m - 190002.5;
    var rjd = 367.0*y;
    rjd -= Math.floor(7.0*(y+Math.floor((m+9.0)/12.0))/4.0);
    rjd += Math.floor(275.0*m/9.0);
    rjd += d;
    rjd += 1721013.5;
    rjd -= 0.5*extra/Math.abs(extra);
    rjd += 0.5;
    return rjd;
}

可视域分析

1).初始化可视域分析
var viewshed3ding = false;//用于是否正在绘制可视域分析
var viewshed3d;//当前可视域对象
var aViewshed3d=[];//保存可视域分析的数组
//初始化可视域对象
viewshed3d= new LSGlobe.LSViewshed3D(viewer);
//将可视域分析对象加入到可是与分析数组中
aViewshed3d.push(viewshed3d);
2).绘制可视域分析
    //初始化鼠标事件
    var ViewAnalysisHandler = new LSGlobe.ScreenSpaceEventHandler(viewer.scene.canvas);
    ViewAnalysisHandler.setInputAction(function (movement) {
        var cartesian=scene.pickGlobe(movement.position);
        var cartographic = LSGlobe.Cartographic.fromCartesian(cartesian);
        var currentClickLon = LSGlobe.Math.toDegrees(cartographic.longitude);
        var currentClickLat = LSGlobe.Math.toDegrees(cartographic.latitude);
        var CurrentTileSet= whichTileset(currentClickLon,currentClickLat);
        //判断点是否在模型上函数和压平获取压平对象相同
        //可视域分析点必须在模型上
        if(CurrentTileSet.length<1){
            //如果点没有点在模型上不在向下走
            return;
        }
        if(cartesian != undefined && !viewshed3ding){
            cartesian.z+=1.5;
            //初始点的高度1.5人眼高度
            viewshed3d.viewerPosition = cartesian;
            viewer.scene.primitives.add(viewshed3d);
            //初始添加一个点
            viewshed3ding = true;
        }else{
            viewshed3d.setPoseByTargetPoint(cartesian);
            //点击第二个点绘制结束
            viewshed3ding = false;
        }
    }, LSGlobe.ScreenSpaceEventType.LEFT_CLICK);
    ViewAnalysisHandler.setInputAction(function (movement) {
        if(viewshed3daction&&viewshed3ding){
            var cartesian = viewer.scene.pickGlobe(movement.endPosition);
            viewshed3d.setPoseByTargetPoint(cartesian);
        }
    }, LSGlobe.ScreenSpaceEventType.MOUSE_MOVE);

3).删除可视域分析
    for(var i=0;i<aViewshed3d.length;i++){
        try{
            viewer.scene.primitives.remove(aViewshed3d[i]);
        }catch(e){
        }
    }
    aViewshed3d=[];-
    viewshed3ding=false;

方量分析

1).初始化方量分析
var cutfill = new LSGlobe.LSCutFill(viewer);
var nodeMesh,tileset;
var poinsTemp;//用于保存绘制的点
var oHeight;
//分析对象
var tempDataSource;
//用于存储方量分析辅助面
var promise = viewer.dataSources.add(new LSGlobe.GeoJsonDataSource("tempDataSource"));
promise.then(function(dataSource) {
    tempDataSource= dataSource;
}).otherwise(function(error){

});

2).使用绘制插件来绘制方量分析面
//注意:绘制方量分析时点必须点在模型上
viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYGON);
viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event){
    //绘制完成后对返回点集合event.positions进行变换
    poinsTemp=event.positions;
    var cartographic = LSGlobe.Cartographic.fromCartesian(poinsTemp[0]);
    var longitude=LSGlobe.Math.toDegrees(cartographic.longitude);
    var latitude=LSGlobe.Math.toDegrees(cartographic.latitude);
    tileset = whichTileset(longitude,latitude);
    if(tileset.length >0){
        setInterval(getProgress, 1000);
    }
    oHeight=((event.maxHeight*1+event.minHeight*1)/2).toFixed(2);
    var oValuePoint = ChangePolygonPosition(event.positions);
    //点集合设置为统一高度
    for (var i = 2; i < oValuePoint.length; i = i + 3) {
        oValuePoint[i] = oHeight;
    }
    //该面是辅助方量分析结果的面,最后方量分析的高度和范围是该面显示的高度和范围
    tempDataSource.entities.add({
        name: '方量分析辅助面',
        id: id,
        polygon: {
            hierarchy: LSGlobe.Cartesian3.fromDegreesArrayHeights(oValuePoint),
            material: new LSGlobe.Color(0.5, 1.0, 1.0, 0.7),
            fill: true,
            outline: true,
            outlineColor: LSGlobe.Color.YELLOW,
            perPositionHeight: true
        }
    });
});
function getProgress(){
    nodeMesh = tileset.NodeMesh;
}
3).开始分析
modelAnalysis(oHeight);
function modelAnalysis(oHeight){
    cutfill.clear();
    cutfill.completeEvent.addEventListener(progress);
    var cartographic = LSGlobe.Cartographic.fromCartesian(poinsTemp[0]);
    cutfill.vertice = nodeMesh;
    cutfill.matLocal = tileset.m_matLocal;
    LSGlobe.Matrix4.inverseTransformation(tileset.m_matLocal,cutfill.matLocalInvert);
    if(oHeight){
        cutfill.zFactor=height;
    }else{
        cutfill.zFactor = cartographic.height;
    }
    //分析的面对象
    var Mypolygon = new LSGlobe.PolygonGeometry({
        polygonHierarchy : new LSGlobe.PolygonHierarchy(poinsTemp),
        perPositionHeight : true
    });
    cutfill.polygon = Mypolygon;
    //采样间隔
    var interval = $(".v-analysis-deg input:checked").val();
    cutfill.sampleGap = interval;
    //执行分析
    cutfill.apply();
}

插件的应用

线面绘制插件

线面插件的引用

1).引用插件css文件
 <link href="Plugin/GeometryEditer/GeometryEditer.css" rel="stylesheet" type="text/css"/>
<!-- 如果想改变插件的位置修改该css文件即可 -->

2).引用插件javascript文件
 <script type="text/javascript" src="Plugin/GeometryEditer/GeometryEditerViewModel.js"></script>
 <script type="text/javascript" src="Plugin/GeometryEditer/GeometryEditer.js"></script>
 <script type="text/javascript" src="Plugin/GeometryEditer/viewerGeometryEditerMixin.js"></script>

3).将插件引入到场景中
 <!-- 在初始化球后的位置添加 -->
 <!-- viewer.extend(viewerGeometryEditerMixin); -->
 <!-- 注意:插件应用图片dragIcon.png位置需要和引用插件页面的位置对应好 -->

线面插件的应用

//绘制线
var material = LSGlobe.Material.fromType(LSGlobe.Material.ColorType);
material.uniforms.color = LSGlobe.Color.fromCssColorString("rgb(0,186,255)");
viewer.geometryEditer.viewModel.defaultPolylineOptions.material = material;
viewer.geometryEditer.viewModel.defaultPolylineOptions.width = 2;
//设置绘制线样式(线的颜色和宽度)
viewer.geometryEditer.viewModel.defaultPolylineOptions.classificationType =LSGlobe.ClassificationType.BOTH;
//设置绘制线的类型 LSGlobe.ClassificationType.BOTH 即可贴地又可贴模型
//可修改 如果绘制空间线需要去掉该句
//LSGlobe.ClassificationType.TERRAIN 仅贴地
//LSGlobe.ClassificationType.CESIUM_3D_TILE 仅贴模型
viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYLINE);
//绘制类型EditAction.CREATE_POLYLINE 绘制线  EditAction.CREATE_POLYGON绘制面
viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event) {
    //绘制完成以后执行事件 返回值event.position 绘制的所有点的集合
});

//绘制面
var material = LSGlobe.Material.fromType(LSGlobe.Material.ColorType);
material.uniforms.color = LSGlobe.Color.fromCssColorString("rgba(0,186,255,0.5)");
viewer.geometryEditer.viewModel.defaultSurfaceOptions.material = material;
viewer.geometryEditer.viewModel.defaultSurfaceOptions.classificationType = LSGlobe.ClassificationType.BOTH;
//设置绘制面的类型 LSGlobe.ClassificationType.BOTH 即可贴地又可贴模型
//该属性可修改 如果绘制空间面需要去掉该句
//LSGlobe.ClassificationType.TERRAIN 仅贴地
//LSGlobe.ClassificationType.CESIUM_3D_TILE 仅贴模型
viewer.geometryEditer.viewModel.setAction(EditAction.CREATE_POLYGON);
viewer.geometryEditer.viewModel.onCreatedEvent.removeAllEventListener();
viewer.geometryEditer.viewModel.onCreatedEvent.addEventListener(function (event){
    //绘制完成以后执行事件 返回值event.position 绘制的所有点的集合
});
//重置绘制过程(即使正在绘制也将被打断)
viewer.geometryEditer.viewModel.stopDrawing();

指北针插件

1).引用插件css文件
<link href="Plugin/Navigation/Navigation.css" rel="stylesheet" type="text/css"/>
<!-- 如果想改变插件的位置修改该css文件即可 -->

2).引用插件javascript文件
<script type="text/javascript" src="Plugin/Navigation/viewerNavigationMixin.js"></script>

3).将插件引入到场景中
viewer.extend(viewerNavigationMixin);

量测插件

1).引用插件css文件
<link href="Plugin/Measure/Measure.css" rel="stylesheet" type="text/css"/>
<!-- 如果想改变插件的位置修改该css文件即可 -->

2).引用插件javascript文件
<script type="text/javascript" src="Plugin/Measure/viewerMeasureMixin.js"></script>

3).将插件引入到场景中
<!-- viewer.extend(viewerMeasureMixin); -->

4).插件的使用
    <!-- 空间距离测量 -->
    viewer.measureEditer.viewModel.Distance();
    <!-- 空间面积测量 -->
    viewer.measureEditer.viewModel.AreaMeasure();
    <!-- 空间高度测量(三角量测) -->
    viewer.measureEditer.viewModel.HeightMeasure();
    <!-- 坐标测量 -->
    viewer.measureEditer.viewModel.Point();
    <!-- 清空所有量测的结果 -->
    viewer.measureEditer.viewModel.ClearAll();
    <!-- 重置量测状态 -->
    viewer.measureEditer.viewModel.ResetState();

模型编辑插件

模型编辑插件的使用主要为了在构建场景,时快速的调整模型位置,所以模型编辑插件只能对实景三维模型和人工模型有效果,其中实景三维模型和3dTiles类型的人工模型只能进行模型的左右和上下移动,lmz格式的人工模型可以进行模型和上下左右移动,还可进行模型绕z轴旋转

1).引用插件css文件
<link href="Plugin/EditBoxGeometry/EditBoxGemetry.css" rel="stylesheet" type="text/css"/>
<!-- 如果想改变插件的位置修改该css文件即可 -->

2).引用插件javascript文件
<script type="text/javascript" src="Plugin/GeometryEditer/GeometryEditerViewModel.js"></script>

3).将插件引入到场景中
viewer.extend(viewerEditBoxGeometryMixin);

4).插件的使用
    1).添加对象
    <!-- 添加某个想要编辑对象到插件中开始编辑(对象必须是人工或实景三维模型) -->
    viewer.boxGeometryEditer.viewModel.editObject="获取到的人工或者实景三维对象";
    2).设置平移模式
    <!-- 只能上下左右平移的对象(实景三维设置该方法) -->
    viewer.boxGeometryEditer.viewModel.Move();
    <!-- 即能上下左右平移也可绕z轴旋转 -->
    viewer.boxGeometryEditer.viewModel.All();
    3).在球中拖动编辑框编辑刚刚插入的编辑对象
    4).编辑状态的保存
    <!-- lmz格式的平移结果在保存场景json的时候(viewer.toJSON())自动保存 -->
    <!-- 3dtiles和实景三维的位置信息,保存在数据的json文件中需要调后台接口修改json文件中的position值 -->
    具体接口
    http://localhost:8080/api/server/1.0.0/update
    5).点击到编辑模型框的外面自动结束编辑状态
5).插件的常用接口
    <!-- 清空编辑状态 -->
    viewer.boxGeometryEditer.viewModel.Cancel();
    <!-- 坐标系显示隐藏 -->
    viewer.boxGeometryEditer.viewModel.CoordinateAxisShow(false/true);
    <!-- 提示文字的显示隐藏 -->
    viewer.boxGeometryEditer.viewModel.toolTip(true/false);

用户系统

用户添加

用户管理

  1. 用户列表获取

    http://localhost:1000/api/user/1.0.0/query

  2. 用户信息修改

    1).修改用户电话号码

    http://localhost:1000/api/user/1.0.0/updatePhone

    2).修改用户昵称

    http://localhost:1000/api/user/1.0.0/updateNickName

    3).用户审核

    http://localhost:1000/api/user/1.0.0/check

用户登录

  1. 用户登录

    http://localhost:1000/api/user/1.0.0/login

  2. 用户登出

    http://localhost:1000/api/user/1.0.0/loginout

系统设置

许可相关

  1. 获取许可的详细信息

    http://localhost:1000/api/license/1.0.0/details

  2. 获取许可码信息

    http://localhost:1000/api/license/1.0.0/getLicenseCode

  3. 许可文件备份下载

    http://localhost:1000/api/license/1.0.0/download

  4. 许可文件上传

    http://localhost:1000/api/license/1.0.0/apply

  5. 许可码和许可地址获取

    http://localhost:1000/api/license/1.0.0/getLicenseConf

权限管理

数据权限管理

  1. 获取权限列表

    http://localhost:1000/api/power/1.0.0/query

  2. 获取之前保存的的数据权限信息

    http://localhost:1000/api/power/1.0.0/getJson

  3. 查询根据token获取当前团队下的用户

    http://localhost:1000/api/team/1.0.0/users

    token是登录成功以后返回的值

  4. 根据token获取当前团队下分组

    http://localhost:1000/api/team/1.0.0/organization

    token是登录成功以后返回的值

  5. 根据token获取当前团队下角色

    http://localhost:1000/api/team/1.0.0/users

    token是登录成功以后返回的值

  6. 初始化权限信息

    该接口需要在操作权限的时候调用一下

    http://localhost:1000/api/power/1.0.0/init

  7. 根据用户token值获取选中团队

    http://localhost:1000/api/team/1.0.0/get

    token是登录成功以后返回的值

  8. 获取当前用户所拥有的权限

    http://localhost:1000/api/power/1.0.0/get

    token登录以后返回的信息

    该接口返回的信息包含该用户对数据是否有数据预览、数据编辑、数据删除、数据权限配置等权限和是否具有场景预览、场景编辑、场景删除、场景权限配置等权,以及具有哪些菜单栏的权限比如:成员管理、分组管理、角色管理