STK + C# + Cesium联合编程(二):CZML文件生成及加载

概述

本文续上一篇博文,上一篇文章中验证了C# .NET Framework (Web Service) + STK + Cesium前端显示的相关技术,本篇通过STK安装附带的Pro Tutorial实例以及Export CZML插件演示如何创建STK场景,创建对象,计算Access,并通过插件输出CZML文件,以及在客户端加载显示场景。

CZML是一种JSON格式的用于描述时间动态图形场景的规范。CZML主要用在Cesium客户端浏览器中显示,其能够描述线、点、广告牌、模型和其他图形基元,并指定它们如何随时间变化。通过CZML扩展资源,可以实现更丰富的场景效果,例如显示STK场景中传感器(Sensor)的探测动态等。

任务目标

本实例在服务端参照STKPro Stutorial给出的实例代码生成STK场景,通过STK的Export CZML插件输出czml格式文件(存储在服务器本地),客户端浏览器通过加载czml文件实现场景的复现。

环境及版本

  • STK 11.6

  • VS2017(C#, .NETFramework 4.8)

  • Cesium-1.99

相关资源

参见本系列上一篇博文:https://blog.csdn.net/wangyulj/article/details/128914391

实现过程

服务端场景生成

代码如下(在STK示例代码基础上增加了注释):

        [WebMethod(Description = "以STK Tutorial场景为例输出czml文件")]
        public void CzmlExport()
        {
            string description = "";  // 计算过程、结果或异常等的描述
            this.m_STKApp = null;
            this.m_STKRoot = null;

            //--------------------------------------------------------
            // 参照STK Pro Tutorial创建场景,并输出czml文件
            //--------------------------------------------------------
            if (!InvokeSTK(out description))
            {
                StkCalCommon.packAndResponse(this.Context, -1, description, "");
                return;
            }

            // 设置场景各计算单位(有可能之前的场景设置不同,最好是重置一遍)
            SetUnits();

            // 设置仿真时间:startTime, stopTime, epoch
            string startTime = "1 Jul 2002 00:00:00.00";
            string stopTime  = "1 Jul 2002 04:00:00.00";
            string epoch     = "1 Jul 2002 00:00:00.00";
            SetSimuTimes(startTime, stopTime, epoch);

            //currently there is no way to set the 2d graphics properties for the scenario listed in the tutorial

            // 创建地面设施
            // AgESTKObjectType Enumeration
            // IAgScenario 接口实现了 IAgStkObject 接口以及 IAgLifetimeInformation 接口
            // 此处用的是 m_STKRoot.CurrentScenario 的 IAgStkObject 接口的 Children 属性的 New() 方法
            // m_STKRoot.CurrentScenario.Children : IAgStkObjectCollection Interface,表示一组STK对象的集合
            IAgFacility baikonur = (IAgFacility)this.m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "Baikonur");
            // IAgFacility.UseTerrain:选择是否使用地形数据自动设置(facility)高度
            baikonur.UseTerrain = false;
            // -- AgEPositionType:属于STK Util库,定了7种位置类型:
            // eCartesian - 笛卡尔坐标:根据对象位置向量的 X、Y 和 Z 分量指定的位置,其中 Z 轴指向北极,X 轴穿过 0 度纬度/0 度经度。
            // eCylindrical - 圆柱:根据半径(极坐标)、经度(以度数从 -360.0 度到 +360.0 度测量)和对象位置矢量的 Z 分量指定的位置。
            // eGeocentric - 地心:以纬度(地球表面上子点的球纬度)、经度和高度指定的位置。
            // eGeodeti - 大地测量:根据纬度(参考椭球法线与赤道平面之间的角度)、经度和高度指定的位置。
            // eSpherical - 球面:根据纬度(地球表面子点的球面纬度)、经度和半径(物体距地球中心的距离)指定的位置。
            // ePlanetocentric - 行星中心:根据纬度(地球表面子点的球面纬度)、经度和高度指定的位置。
            // ePlanettodetic - 行星大地测量:根据纬度(参考椭球法线与赤道平面之间的角度)、经度和高度指定的位置。
            // -- 此处用的是 ePlanetodetic,根据维度(参考椭球法线与赤道平面之间的角度)、维度和高度指定位置
            IAgPlanetodetic planetodetic = (IAgPlanetodetic)baikonur.Position.ConvertTo(AgEPositionType.ePlanetodetic);
            planetodetic.Lat = 48.0;
            planetodetic.Lon = 55.0;
            planetodetic.Alt = 0.0;
            baikonur.Position.Assign(planetodetic);

            // 注意在运算中在特定的STK对象(例如IAgFacility)与通用的STK对象(IAgStkObject)之间的转换
            ((IAgStkObject)baikonur).ShortDescription = "Launch Site";
            ((IAgStkObject)baikonur).LongDescription = "Launch site in Kazakhstan. Also known as Tyuratam.";

            // 继续创建两个地面站(设施)
            IAgFacility perth = (IAgFacility)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "Perth");
            IAgFacility wallops = (IAgFacility)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eFacility, "Wallops");

            perth.UseTerrain = false;
            planetodetic = (IAgPlanetodetic)perth.Position.ConvertTo(AgEPositionType.ePlanetodetic);
            planetodetic.Lat = -31.0;
            planetodetic.Lon = 116.0;
            planetodetic.Alt = 0;
            perth.Position.Assign(planetodetic);

            ((IAgStkObject)perth).ShortDescription = "Australian Tracking Station";

            wallops.UseTerrain = false;
            planetodetic = (IAgPlanetodetic)wallops.Position.ConvertTo(AgEPositionType.ePlanetodetic);
            planetodetic.Lat = 37.8602;
            planetodetic.Lon = -75.5095;
            planetodetic.Alt = -0.0127878;
            wallops.Position.Assign(planetodetic);

            ((IAgStkObject)wallops).ShortDescription = "NASA Launch Site/Tracking Station";

            // 从现有数据库添加地面设施(示例代码)
            // 执行connct命令示例
            AGI.STKUtil.AgExecCmdResult facDbResult = (AgExecCmdResult)m_STKRoot.ExecuteCommand("GetDirectory / Database Facility");
            string facDataDir = facDbResult[0];     // 取返回结果的第一个元素
            // @符号:可使字符串不必使用转义序列
            string filelocation = facDataDir + @"\stkFacility.fd";
            string command = "ImportFromDB * Facility \"" + filelocation + "\"Class Facility SiteName \"Santiago Station AGO 3 STDN AGO3\" Network \"NASA NEN\" Rename Santiago";
            m_STKRoot.ExecuteCommand(command);
            command = "ImportFromDB * Facility \"" + filelocation + "\"Class Facility SiteName \"White Sands\" Network \"Other\" Rename WhiteSands";
            m_STKRoot.ExecuteCommand(command);

            // 获取刚刚创建的两个 Facility 对象
            IAgFacility santiago = (IAgFacility)m_STKRoot.CurrentScenario.Children["Santiago"];
            IAgFacility whitesands = (IAgFacility)m_STKRoot.CurrentScenario.Children["WhiteSands"];

            // ***.Graphics : IAgFaGraphics,修改地面设置的(显示)颜色
            // 为啥不用 FromArgb 方法之外的其他形式,直接一个 int 参数实在是不直观
            // 可以注释掉下面4行,实际效果比价差,颜色暗,还不如默认的颜色,也可以根据实际效果调整
            //baikonur.Graphics.Color = Color.Lime;
            //perth.Graphics.Color = Color.FromArgb(16777215);
            //wallops.Graphics.Color = Color.FromArgb(13421772);
            //santiago.Graphics.Color = Color.FromArgb(88888);
            //whitesands.Graphics.Color = Color.FromArgb(1234567);

            // 创建 Target 对象,与创建 Facility 对象基本一致
            IAgTarget iceberg = (IAgTarget)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eTarget, "Iceberg");
            iceberg.UseTerrain = false;
            planetodetic = (IAgPlanetodetic)iceberg.Position.ConvertTo(AgEPositionType.ePlanetodetic);
            planetodetic.Lat = 74.91;
            planetodetic.Lon = -74.5;
            planetodetic.Alt = 0.0;
            iceberg.Position.Assign(planetodetic);

            ((IAgStkObject)iceberg).ShortDescription = "Only the tip.";

            // 创建 Ship 对象
            // AgShip类实现了如下接口:IAgGreatArcVehicle, IAgLifetimeInformation, IAgProvideSpatialInfo, IAgShip, IAgStkObject
            IAgShip cruise = (IAgShip)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eShip, "Cruise");
            // 设置路径预报器,SetRouteType()是IAgGreatArcVehicle接口方法
            // 包括AgGroundVehicle, AgAircraft, AgShip等三个COM类(CoClass)实现了 IAgGreatArcVehicle 接口
            // AgEVePropagatorType,因为涵盖了卫星的轨道预报器,有点多,定义了18种,其中大弧预报器用于飞机、船只、地面车辆。
            cruise.SetRouteType(AgEVePropagatorType.ePropagatorGreatArc);
            IAgVePropagatorGreatArc greatArc = (IAgVePropagatorGreatArc)cruise.Route;
            IAgCrdnEventIntervalSmartInterval smartInterval = greatArc.EphemerisInterval;
            // 下面方法的第二个参数没有用 stopTime,而是STK计算模块根据Ship对象的路径及速度(速度应该是一个系统默认值)
            // 计算,根据czml文件的输出结果判断,Ship沿路径点需要5天才能到达目的地,而其他对象的计算时间均约定为4小时,
            // 所以在czml文件的仿真时间中,是取场景中各对象的仿真时间的最大值为
            smartInterval.SetExplicitInterval(startTime, smartInterval.FindStopTime());
            greatArc.Method = AgEVeWayPtCompMethod.eDetermineTimeAccFromVel;

            // 设置 Ship 的路径点
            AddWaypoint(greatArc.Waypoints, 44.1, -8.5, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 51.0, -26.6, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 52.1, -40.1, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 60.2, -55.0, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 68.2, -65.0, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 72.5, -70.1, 0.0, .015, 0.0);
            AddWaypoint(greatArc.Waypoints, 74.9, -74.5, 0.0, .015, 0.0);

            // AgEVeAttitude(姿态类型)定义了两个值:eAttitudeRealtime, eAttitudeStandard
            // 设置大弧预报器的各参数,然后进行路径计算
            cruise.SetAttitudeType(AgEVeAttitude.eAttitudeStandard);
            IAgVeRouteAttitudeStandard attitude = (IAgVeRouteAttitudeStandard)cruise.Attitude;
            attitude.Basic.SetProfileType(AgEVeProfile.eProfileECFVelocityAlignmentWithRadialConstraint);
            cruise.Graphics.WaypointMarker.IsWaypointMarkersVisible = true;
            cruise.Graphics.WaypointMarker.IsTurnMarkersVisible = true;
            greatArc.Propagate();    // 轨道预报(计算)

            //===========================================================================
            // 创建卫星,TDRS: Tracking & Data Relay Satellite
            // IAgSatellite 其实只有两个接口方法:SetAttitudeType, SetPropagatorType,分别指定姿态和预报器类型
            // 相比 IAgShip,IAgSatellite 仅实现了三个接口IAgStkObject,IAgLifetimeInformation,IAgProvideSpatialInfo
            IAgSatellite tdrs = (IAgSatellite)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eSatellite, "TDRS");
            // 设置轨道预报器(类型),二体(运动)预报器的应用实例代码
            tdrs.SetPropagatorType(AgEVePropagatorType.ePropagatorTwoBody);
            // 二体问题,或者开普勒运动,预报器只考虑来自地球的引力,将其建模为一个质点
            IAgVePropagatorTwoBody twobody = (IAgVePropagatorTwoBody)tdrs.Propagator;

            // IAgOrbitState:系统定义了7种 Orbit State(轨道状态的坐标类型):
            // Cartesian, Classical, Delaunay, Equinoctial, Geodetic, MixedSpherical, Spherical
            IAgOrbitStateClassical classical = (IAgOrbitStateClassical)twobody.InitialState.Representation.ConvertTo(AgEOrbitStateType.eOrbitStateClassical);
            classical.CoordinateSystemType = AgECoordinateSystem.eCoordinateSystemJ2000;
            smartInterval = twobody.EphemerisInterval;
            smartInterval.SetExplicitInterval(startTime, stopTime);
            twobody.Step = 60;    // 秒(s)
            // AgEClassicalLocation预定义值包括:
            //     eLocationArgumentOfLatitude - 使用纬度参数指定航天器位置。
            //     eLocationExcentricAnomaly - 使用 Eccentric Anomaly 指定航天器位置。
            //     eLocationMeanAnomaly - 使用平均异常来指定航天器位置。
            //     eLocationTimePastAN - 使用经过升交点的时间指定航天器位置。
            //     eLocationTimePastPerigee - 使用 Time Past Perigee 指定航天器位置。
            //     eLocationTrueAnomaly - 使用 True Anomaly 指定航天器位置。
            classical.LocationType = AgEClassicalLocation.eLocationTrueAnomaly;
            IAgClassicalLocationTrueAnomaly trueAnomaly = (IAgClassicalLocationTrueAnomaly)classical.Location;
            // ture anomaly: 真近点角
            trueAnomaly.Value = 178.845262;

            // 利用轨道周期和偏心率确定轨道形状
            classical.SizeShapeType = AgEClassicalSizeShape.eSizeShapePeriod;
            // IAgClassicalSizeShapePeriod:使用周期和偏心率指定(卫星的)轨道大小和形状
            IAgClassicalSizeShapePeriod period = (IAgClassicalSizeShapePeriod)classical.SizeShape;
            // Eccentricity: 偏心率,离心率
            period.Eccentricity = 0.0;
            // 轨道周期,基于假定的二体运动的轨道周期
            period.Period = 86164.090540;
            // classical.Orientation: IAgClassicalOrientation Interface
            // 近地点角:从升交点到在卫星运动方向和轨道平面中测量的偏心矢量(轨道最低点)的角度
            classical.Orientation.ArgOfPerigee = 0.0;
            // 轨道倾角
            classical.Orientation.Inclination = 0.0;
            // 升交点类型包括:LAN(Longitude of Ascending Node,升交点经度), RAAN(升交点赤经)
            // 升交点经度:卫星从南向北穿过惯性赤道的地球固定经度(the Earth-fixed longitude where the satellite crosses the inertial equator form south to north)
            // LAN : the Earth-fixed longitude where the satellite crosses the inertial equator form south to north
            // RAAN: the angle from the inertial X axis to the ascending node measured in a right-handed sense about the inertial Z axis in the equatorial plane
            classical.Orientation.AscNodeType = AgEOrientationAscNode.eAscNodeLAN;
            IAgOrientationAscNodeLAN lan = (IAgOrientationAscNodeLAN)classical.Orientation.AscNode;
            // 升交点经度
            lan.Value = 259.999982;

            twobody.InitialState.Representation.Assign(classical);
            // 执行轨道计算(预报)
            twobody.Propagate();

            // 从数据库(文件)创建卫星
            AGI.STKUtil.AgExecCmdResult satDbResult = (AgExecCmdResult)m_STKRoot.ExecuteCommand("GetDirectory / Database Satellite");
            string satDataDir = satDbResult[0];
            filelocation = satDataDir + @"\stkSatDB.sd";
            // STK示例代码此处有一个bug,Rename的参数“TDRS 3”与后续代码实际使用的名称不一致,少了一个下划线
            // 在CustomApplications项目中的代码是正确的(Automation中的代码有bug)
            //command = "ImportFromDB * Satellite \"" + filelocation + "\" Rename \"TDRS 3\" Propagate On CommonName \"TDRS 3\"";
            command = "ImportFromDB * Satellite \"" + filelocation + "\" Rename TDRS_3 Propagate On CommonName \"TDRS 3\"";
            m_STKRoot.ExecuteCommand(command);
            // 获取刚刚从数据库文件创建的卫星对象
            IAgSatellite tdrsC = (IAgSatellite)m_STKRoot.CurrentScenario.Children["TDRS_3"];
            // 需要重新计算(预报)报道,用的是SGP4轨道预报器(简化的通用摄动预报器,与TLE一起使用)
            IAgVePropagatorSGP4 sgp4 = (IAgVePropagatorSGP4)tdrsC.Propagator;
            smartInterval = sgp4.EphemerisInterval;
            smartInterval.SetExplicitInterval(startTime, stopTime);

            // 创建ERS(Earth Resources Satellite)卫星
            IAgSatellite ers1 = (IAgSatellite)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eSatellite, "ERS1");
            // j4: J4摄动(二阶)预报器,考虑了由于地球扁率引起的轨道要素的长期变化
            ers1.SetPropagatorType(AgEVePropagatorType.ePropagatorJ4Perturbation);
            IAgVePropagatorJ4Perturbation j4 = (IAgVePropagatorJ4Perturbation)ers1.Propagator;
            smartInterval = j4.EphemerisInterval;
            smartInterval.SetExplicitInterval(startTime, stopTime);
            j4.Step = 60.00;

            // J4预报器的参数设置还蛮多的
            IAgOrbitState j4Orb = j4.InitialState.Representation as IAgOrbitState;
            j4Orb.Epoch = startTime;
            classical = (IAgOrbitStateClassical)j4.InitialState.Representation.ConvertTo(AgEOrbitStateType.eOrbitStateClassical);
            classical.CoordinateSystemType = AgECoordinateSystem.eCoordinateSystemJ2000;
            // ture anomaly: 真近点角
            classical.LocationType = AgEClassicalLocation.eLocationTrueAnomaly;
            trueAnomaly = (IAgClassicalLocationTrueAnomaly)classical.Location;
            trueAnomaly.Value = 0.0;
            // 通过指定半长轴(Semimajor Axis)和偏心率以确定轨道形状
            classical.SizeShapeType = AgEClassicalSizeShape.eSizeShapeSemimajorAxis;
            IAgClassicalSizeShapeSemimajorAxis semi = (IAgClassicalSizeShapeSemimajorAxis)classical.SizeShape;
            semi.SemiMajorAxis = 7163.14;   // 半长轴
            semi.Eccentricity = 0.0;    // 偏心率/离心率
            classical.Orientation.ArgOfPerigee = 0.0;   // 近地点角
            classical.Orientation.AscNodeType = AgEOrientationAscNode.eAscNodeLAN;
            lan = (IAgOrientationAscNodeLAN)classical.Orientation.AscNode;
            lan.Value = 99.38;  // 升交点经度
            classical.Orientation.Inclination = 98.50;  // 轨道倾角

            j4.InitialState.Representation.Assign(classical);
            j4.Propagate();

            // IAgSaGraphics Interface : ers1.Graphics,卫星的2D图形属性
            // IAgVeGfxPasses Interface : ers1.Graphics.Passes,用于设置卫星轨迹显示的接口
            // 看示例代码中的语句,应该是设置了Both,就没有必要再设置Descending,前一句代码多余(冗余)
            // 如果只是需要生成数据报告,则可以简化或忽略对2D或3D属性的设置
            ers1.Graphics.Passes.VisibleSides = AgEVeGfxVisibleSides.eVisibleSidesDescending;
            ers1.Graphics.Passes.VisibleSides = AgEVeGfxVisibleSides.eVisibleSidesBoth;

            // 新建一个空间站(Shuttle),使用J4预报器计算轨道
            // 参考上一卫星创建的注释及说明
            IAgSatellite shuttle = (IAgSatellite)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eSatellite, "Shuttle");
            shuttle.SetPropagatorType(AgEVePropagatorType.ePropagatorJ4Perturbation);
            j4 = (IAgVePropagatorJ4Perturbation)shuttle.Propagator;
            smartInterval = j4.EphemerisInterval;
            // 此处与原代码有不同,原代码的 stopTime 仅计算到 3 点
            smartInterval.SetExplicitInterval(startTime, stopTime);
            j4.Step = 60.00;

            j4Orb = j4.InitialState.Representation as IAgOrbitState;
            j4Orb.Epoch = startTime;
            classical = (IAgOrbitStateClassical)j4.InitialState.Representation.ConvertTo(AgEOrbitStateType.eOrbitStateClassical);
            classical.CoordinateSystemType = AgECoordinateSystem.eCoordinateSystemJ2000;
            classical.LocationType = AgEClassicalLocation.eLocationTrueAnomaly;
            trueAnomaly = (IAgClassicalLocationTrueAnomaly)classical.Location;
            trueAnomaly.Value = 0.0;    // 真近点角
            // Size Shape Type与前一创建实例不一样
            //     eSizeShapeAltitude - Apogee and Perigee Altitude(远地点和近地点高度).
            //     eSizeShapeMeanMotion - Mean Motion and Eccentricity(平均运动和离心率.
            //     eSizeShapePeriod - Period and Eccentricity(周期和偏心率).
            //     eSizeShapeRadius - Apogee and Perigee Radius(远地点和近地点半径).
            //     eSizeShapeSemimajorAxis - Semimajor Axis and Eccentricity(半长轴和偏心率).
            // 通过指定远地点和近地点高度确定轨道形状
            classical.SizeShapeType = AgEClassicalSizeShape.eSizeShapeAltitude;
            IAgClassicalSizeShapeAltitude altitude = (IAgClassicalSizeShapeAltitude)classical.SizeShape;
            altitude.ApogeeAltitude = 370.4;    // 远地点高度
            altitude.PerigeeAltitude = 370.4;   // 近地点高度
            classical.Orientation.ArgOfPerigee = 0.0;   // 近地点角
            classical.Orientation.AscNodeType = AgEOrientationAscNode.eAscNodeLAN;
            lan = (IAgOrientationAscNodeLAN)classical.Orientation.AscNode;
            lan.Value = -151.0;  // 升交点经度
            classical.Orientation.Inclination = 28.5;  // 轨道倾角

            j4.InitialState.Representation.Assign(classical);
            j4.Propagate();

            // Modify Shuttle Contours(轮廓/等高线),这个属性估计czml不会输出
            shuttle.Graphics.SetAttributesType(AgEVeGfxAttributes.eAttributesBasic);
            // IAgVeGfxAttributesOrbit : 设置卫星2D属性的接口,本接口又借助接口IAgVeGfxAttributesBasic(的实现)提供相关功能
            IAgVeGfxAttributesOrbit orbitgfx = (IAgVeGfxAttributesOrbit)shuttle.Graphics.Attributes;
            orbitgfx.Line.Style = AgELineStyle.eDashed;
            // 下面这个关键字‘Plus’没有预定义枚举类型,就怕这种参数,最好是别用到
            orbitgfx.MarkerStyle = "Plus";

            // contours : 轮廓线/等高线,这个等高线的设置后的效果在图中没有看出来
            // 轮廓线的设置在czml文件也未输出
            IAgVeGfxElevContours contours = (IAgVeGfxElevContours)shuttle.Graphics.ElevContours;
            IAgVeGfxElevationsCollection elevations = contours.Elevations;
            elevations.RemoveAll();
            elevations.AddLevelRange(0, 50, 10);

            for (int i = 0; i < elevations.Count; i++)
            {
                IAgVeGfxElevationsElement elem = elevations[i];
                elem.DistanceVisible = false;
                elem.LineStyle = AgELineStyle.eDotDashed;
                elem.LineWidth = AgELineWidth.e3;
            }

            // 下面几条语句不起作用,在本例中忽略
            //contours.IsVisible = true;
            //contours.Fill = true; 此版本没有Fill属性(可设置)
            //contours.FillTranslucency = 80.0;

            //=========================================================================
            //Again we don't have the capability in OM to handle a second graphics window and modify it's properties
            // 创建面目标(AreaTarget)
            IAgAreaTarget searchArea = (IAgAreaTarget)m_STKRoot.CurrentScenario.Children.New(AgESTKObjectType.eAreaTarget, "SearchArea");
            // 设置AreaTarget的2D属性
            IAgATGraphics atGfx = searchArea.Graphics;
            atGfx.MarkerStyle = "None";
            atGfx.Inherit = false;
            atGfx.LabelVisible = false;
            atGfx.CentroidVisible = false;

            searchArea.UseTerrainData = false;
            searchArea.AutoCentroid = false;
            // 有另种方式指定AreaTarget的边界:
            //     eEllipse - 椭圆
            //     ePattern - 类似于多边形,以一组点坐标(经纬度)定义边界
            searchArea.AreaType = AgEAreaType.ePattern;
            IAgAreaTypePatternCollection patterns = (IAgAreaTypePatternCollection)searchArea.AreaTypeData;
            patterns.Add(78.4399, -77.6125);
            patterns.Add(77.7879, -71.1578);
            patterns.Add(74.5279, -69.0714);
            patterns.Add(71.6591, -69.1316);
            patterns.Add(70.0291, -70.8318);
            patterns.Add(71.9851, -76.3086);

            // Spherical : 球面坐标定义:维度、经度、球半径(此处为地球半径)
            // 此处指定的是AreaTarget的中心?难道不应该自动计算吗?
            // 经往回看,此处的坐标是前面目标对象(Target对象)Iceberg的坐标
            IAgSpherical sphere = (IAgSpherical)searchArea.Position.ConvertTo(AgEPositionType.eSpherical);
            sphere.Lat = 74.9533;
            sphere.Lon = -74.5482;
            sphere.Radius = 6358.186790;
            searchArea.Position.Assign(sphere);

            // 访问计算,从ers1 到 searchArea
            IAgStkAccess access = ((IAgStkObject)ers1).GetAccessToObject((IAgStkObject)searchArea);
            access.ComputeAccess();

            // 最怕的就是这种指定的字符串,而不是预定义的字符串,其他的如何知道?
            IAgDataPrvInterval interval = (IAgDataPrvInterval)access.DataProviders["Access Data"];

            // IAgDrResult : Provides methods to access the results returned by the data provider
            IAgDrResult result = (IAgDrResult)interval.Exec(startTime, stopTime);

            //with the result returned, the user can use the data any way they prefer.
            // 此处示例代码省略了关于如何使用数据

            // 清除当前的Access数据
            //access.RemoveAccess();

            // 给卫星(ERS1)添加一个传感器
            IAgSensor horizon = (IAgSensor)m_STKRoot.CurrentScenario.Children["ERS1"].Children.New(AgESTKObjectType.eSensor, "Horizon");
            // eSnSimpleConic : 简单圆锥:由指定的圆锥角定义
            // AgESnPattern中定义了 7 种类型的传感器(天线)模式
            horizon.SetPatternType(AgESnPattern.eSnSimpleConic);
            IAgSnSimpleConicPattern simpleConic = (IAgSnSimpleConicPattern)horizon.Pattern;
            simpleConic.ConeAngle = 90; // 圆锥角
            // AgESnPointing中定义了 9 种类型的(传感器天线)跟踪模式
            horizon.SetPointingType(AgESnPointing.eSnPtFixed);
            IAgSnPtFixed fixedPt = (IAgSnPtFixed)horizon.Pointing;
            // azEl : 方位角/仰角(az:Azimuth, El:Elevation)
            IAgOrientationAzEl azEl = (IAgOrientationAzEl)fixedPt.Orientation.ConvertTo(AgEOrientationType.eAzEl);
            azEl.Elevation = 90;
            azEl.AboutBoresight = AgEAzElAboutBoresight.eAzElAboutBoresightRotate;
            fixedPt.Orientation.Assign(azEl);

            //removing the ers1 elevcontours from the 2d window
            // 移除 ERS1 有关轮廓线的设置(设置为不可见)
            // 此代码对于 czml 输出没有影响
            contours.IsVisible = false;

            // 继续给卫星(ERS1)添加另一个传感器
            IAgSensor downlink = (IAgSensor)m_STKRoot.CurrentScenario.Children["ERS1"].Children.New(AgESTKObjectType.eSensor, "Downlink");

            // eSnHalfPower : 半功率(Half Power),模拟抛物面天线
            downlink.SetPatternType(AgESnPattern.eSnHalfPower);
            IAgSnHalfPowerPattern halfpower = (IAgSnHalfPowerPattern)downlink.Pattern;
            halfpower.Frequency = .85;  // 频率
            halfpower.AntennaDiameter = 1.0;    // 天线口径(m)

            // 设置传感器的目标对象,可与前面的 eSnPtFixed 比较
            downlink.SetPointingType(AgESnPointing.eSnPtTargeted);
            IAgSnPtTargeted targeted = (IAgSnPtTargeted)downlink.Pointing;
            // Boresight : 视轴类型
            targeted.Boresight = AgESnPtTrgtBsightType.eSnPtTrgtBsightTracking;
            // 可一次指定多个 targets
            IAgSnTargetCollection targets = targeted.Targets;
            targets.Add("Facility/Baikonur");
            targets.Add("Facility/WhiteSands");
            targets.Add("Facility/Perth");
            targets.AddObject((IAgStkObject)santiago);
            targets.Add(((IAgStkObject)wallops).Path);

            // 为地面设施(地面站Wallops)创建一个传感器
            IAgSensor fiveDegElev = (IAgSensor)m_STKRoot.CurrentScenario.Children["Wallops"].Children.New(AgESTKObjectType.eSensor, "FiveDegElev");
            // eSnComplexConic : 复杂圆锥,二次曲线,由指定的内半角和外半角以及最小和最大时钟角定义
            fiveDegElev.SetPatternType(AgESnPattern.eSnComplexConic);
            IAgSnComplexConicPattern complexConic = (IAgSnComplexConicPattern)fiveDegElev.Pattern;
            complexConic.InnerConeHalfAngle = 0;    // 内半角
            complexConic.OuterConeHalfAngle = 85;   // 外半角
            complexConic.MinimumClockAngle = 0;     // 最小时钟角
            complexConic.MaximumClockAngle = 360;   // 最大时钟角

            // eSnPtFixed : 设置步骤与前述同
            fiveDegElev.SetPointingType(AgESnPointing.eSnPtFixed);
            fixedPt = (IAgSnPtFixed)fiveDegElev.Pointing;
            azEl = (IAgOrientationAzEl)fixedPt.Orientation.ConvertTo(AgEOrientationType.eAzEl);
            azEl.Elevation = 90;
            azEl.AboutBoresight = AgEAzElAboutBoresight.eAzElAboutBoresightRotate;
            fixedPt.Orientation.Assign(azEl);

            // Graphics相关的设置与2D显示有关
            fiveDegElev.Graphics.Projection.DistanceType = AgESnProjectionDistanceType.eConstantAlt;
            IAgSnProjDisplayDistance dispDistance = (IAgSnProjDisplayDistance)fiveDegElev.Graphics.Projection.DistanceData;
            dispDistance.Max = 785.248;
            dispDistance.Min = 0;
            dispDistance.NumberOfSteps = 1;

            // 重新进行轨道计算?是的,重新计算了卫星 ERS1 的轨道
            j4 = (IAgVePropagatorJ4Perturbation)ers1.Propagator;
            smartInterval = j4.EphemerisInterval;
            // 参数与前述不同,前面直接用的 startTime,此处为 FindStartTime()
            // 第二个参数为啥是前面的 startTime ?哦,不是,是第二天,计算了 24h
            smartInterval.SetExplicitInterval(smartInterval.FindStartTime(), "2 Jul 2002 00:00:00.000");
            j4.Propagate();

            // 定制了计算时间段及其显示(属性)
            ers1.Graphics.SetAttributesType(AgEVeGfxAttributes.eAttributesCustom);
            IAgVeGfxAttributesCustom customAtt = (IAgVeGfxAttributesCustom)ers1.Graphics.Attributes;
            IAgVeGfxInterval gfxInterval = customAtt.Intervals.Add("1 Jul 2002 11:30:00.000", "1 Jul 2002 12:00:00.000");
            gfxInterval.GfxAttributes.Color = Color.FromArgb(15649024); //EEC900
            gfxInterval.GfxAttributes.IsVisible = true;
            gfxInterval.GfxAttributes.Inherit = true;

            gfxInterval = customAtt.Intervals.Add("1 Jul 2002 23:30:00.000", "1 Jul 2002 24:00:00.000");
            gfxInterval.GfxAttributes.Color = Color.FromArgb(11680494); //B23AEE
            gfxInterval.GfxAttributes.IsVisible = true;
            gfxInterval.GfxAttributes.Inherit = true;

            // 设置(卫星 ERS1 的追踪目标),此处设置的似乎是 Graphics 属性,与真正的Access计算无关
            // IAgSaGraphics : ers1.Graphics
            // AgEVeGfxAttributes.eAttributesAccess : Display based on access periods.
            ers1.Graphics.SetAttributesType(AgEVeGfxAttributes.eAttributesAccess);
            IAgVeGfxAttributesAccess gfxAccess = (IAgVeGfxAttributesAccess)ers1.Graphics.Attributes;

            gfxAccess.AccessObjects.Add("Facility/Wallops");
            gfxAccess.AccessObjects.Add("Facility/Santiago");
            gfxAccess.AccessObjects.Add("Facility/Baikonur");
            gfxAccess.AccessObjects.Add("Facility/Perth");
            gfxAccess.AccessObjects.Add(((IAgStkObject)whitesands).Path);

            IAgVeGfxAttributesOrbit orbitGfx = (IAgVeGfxAttributesOrbit)gfxAccess.NoAccess;
            orbitGfx.IsVisible = true;
            orbitGfx.Inherit = false;
            orbitGfx.IsGroundMarkerVisible = false;
            orbitGfx.IsOrbitMarkerVisible = false;

            // horizon是卫星 ERS1 的传感器
            IAgDisplayTm horizonDispTm = (IAgDisplayTm)horizon;
            horizonDispTm.SetDisplayStatusType(AgEDisplayTimesType.eDuringAccess);
            IAgDuringAccess duringAccess = (IAgDuringAccess)horizonDispTm.DisplayTimesData;

            IAgObjectLinkCollection accessObjects = duringAccess.AccessObjects;
            accessObjects.Add("Facility/Wallops");
            accessObjects.Add("Facility/Santiago");
            accessObjects.Add("Facility/Baikonur");
            accessObjects.AddObject((IAgStkObject)perth);
            accessObjects.Add(((IAgStkObject)whitesands).Path);

            // 可见性计算,from(卫星ERS1的)传感器 horizon 到地面站 baikonur,并没有计算到所有对象的Access!
            // 为什么在完成了Access计算之后再进行约束设置?
            access = ((IAgStkObject)horizon).GetAccessToObject((IAgStkObject)baikonur);
            access.ComputeAccess();

            IAgAccessCnstrMinMax minMax = (IAgAccessCnstrMinMax)horizon.AccessConstraints.AddConstraint(AgEAccessConstraints.eCstrSunElevationAngle);
            minMax.EnableMin = true;
            minMax.Min = 10;
            //minMax.Min = 5;
            //minMax.Min = 0;
            //minMax.Min = 15;
            //minMax.Min = 20;

            horizon.AccessConstraints.RemoveConstraint(AgEAccessConstraints.eCstrSunElevationAngle);

            minMax = (IAgAccessCnstrMinMax)horizon.AccessConstraints.AddConstraint(AgEAccessConstraints.eCstrRange);
            minMax.EnableMax = true;
            minMax.Max = 2000;
            //minMax.Max = 1500;
            //minMax.Max = 1000;
            //minMax.Max = 500;

            horizon.AccessConstraints.RemoveConstraint(AgEAccessConstraints.eCstrRange);

            // 清除Access计算
            //access.RemoveAccess();

            // 重置仿真时间(到开始时刻)
            ((IAgAnimation)this.m_STKRoot).Rewind();

            //---------------------------------------------------------------------------
            // export CZML
            // 语法:ExportCZML <ScenarioPath> "<OutputFilePath>" {3D Model Server URL}
            // 要点:1)如果目标文件已经存在,则STK会覆盖现有文件;
            //       2)必须指定{3D Model Server URL},否则失败,可以在后续对czml文件执行处理替换
            filelocation = StkCalCommon.g_czmlLocalPath + "stkprotutorial.czml";
            command = "ExportCZML * \"" + filelocation + "\" " + @"http://assets.agi.com/models/";
            m_STKRoot.ExecuteCommand(command);
            //---------------------------------------------------------------------------

            // 关闭场景,释放资源
            ReleaseSTKR();

            string czmlURL = StkCalCommon.g_czmlURL + "stkprotutorial.czml";
            StkCalCommon.packAndResponse(this.Context, 0, "成功执行操作,生成CZML文件。", czmlURL);
        }

代码中的其他相关方法列表如下:

统一进行STK进程的初始化管理(InvokeSTK方法)

        // 获取或创建STK进程,并根据需要新建场景
        public bool InvokeSTK(out string description, bool newScenario = true)
        {
            string logging = "---- " + DateTime.Now + " ----\n";
            logging += "STK应用初始化。\n";
            this.m_STKApp = null;
            this.m_STKRoot = null;
            description = "";
            // 获取或创建STK应用(AgUiApplication对象)
            try
            {
                logging += "Looking for an instance of STK...\n";
                // 获取已有STK运行实例
                this.m_STKApp = Marshal.GetActiveObject("STK11.Application") as AgUiApplication;
            }
            catch (System.Runtime.InteropServices.COMException ex1)
            {
                logging += "Failed in looking for an instance of STK.\n";
                logging += ex1.Message + "\n";
                // 获取已有STK实例失败,创建一个新的STK实例
                Guid clsID = typeof(AgUiApplicationClass).GUID;
                Type oType = Type.GetTypeFromCLSID(clsID);
                try
                {
                    logging += "Try to create an instance of STK...\n";
                    StkCalCommon.loggingToFile(logging);
                    this.m_STKApp = Activator.CreateInstance(oType) as AgUiApplication;

                    logging += "Try to load STK personality.\n";
                    StkCalCommon.loggingToFile(logging);
                    // 获取用户设置(此过程应该是执行了有关license的检测)
                    this.m_STKApp.LoadPersonality("STK");
                }
                catch (System.Runtime.InteropServices.COMException ex2)
                {
                    logging += "Failed in creating STK instance or loading STK personality.\n";
                    logging += ex2.Message + "\n";
                    StkCalCommon.loggingToFile(logging);
                    this.m_STKApp = null;
                    description = "获取或创建STK应用实例失败1。\n" + ex2.Message;
                    return false;
                }
            }
            // 加载STK Root(IAgStkObjectRoot接口对象)
            if (!(this.m_STKApp is null))
            {
                try
                {
                    logging += "Loading STK root object (IAgStkObjectRoot).\n";
                    this.m_STKRoot = (IAgStkObjectRoot)this.m_STKApp.Personality2;
                    description = "成功创建STK用户应用并获取STK Root对象。";
                }
                catch (Exception ex3)
                {
                    logging += "加载STK Root Object失败。\n";
                    logging += ex3.Message + "\n";
                    StkCalCommon.loggingToFile(logging);
                    description = "加载STK Root Object失败。\n" + ex3.Message;
                    Marshal.ReleaseComObject(this.m_STKApp);
                    return false;
                }
            }

            if ((this.m_STKApp is null) || (this.m_STKRoot is null))
            {
                // 未能初始化STK应用
                logging += "未能初始化STK应用。\n";
                StkCalCommon.loggingToFile(logging);
                description = "未能初始化STK应用。";
                return false;
            }

            logging += "成功获取或创建了STK应用(AgUiApplication),并获取STK Root对象(IAgStkObjectRoot).\n";
            StkCalCommon.loggingToFile(logging);

            if (newScenario)
            {
                // 创建场景
                // 关闭当前(可能有的)场景
                this.m_STKRoot.CloseScenario();
                // 新建场景
                this.m_STKRoot.NewScenario(this.defualtScenName);
            }

            return true;
        }

其他方法:

       // 释放STK相关进程(资源)
        public void ReleaseSTKR(bool closeSecenario = true)
        {
            // 关闭场景
            this.m_STKRoot.CloseScenario();
            // 退出并关闭STK应用(可选)
            Marshal.ReleaseComObject(this.m_STKRoot);
            Marshal.ReleaseComObject(this.m_STKApp);
            this.m_STKApp = null;
            this.m_STKRoot = null;
        }
        // 设置当前场景单位
        private void SetUnits()
        {
            IAgUnitPrefsDimCollection dimensions = this.m_STKRoot.UnitPreferences;
            // 首先重置,然后设置
            dimensions.ResetUnits();
            dimensions.SetCurrentUnit("DateFormat", "UTCG");
            dimensions.SetCurrentUnit("DistanceUnit", "km");
            dimensions.SetCurrentUnit("TimeUnit", "sec");
            dimensions.SetCurrentUnit("AngleUnit", "deg");
            dimensions.SetCurrentUnit("MassUnit", "kg");
            dimensions.SetCurrentUnit("PowerUnit", "dbw");
            dimensions.SetCurrentUnit("FrequencyUnit", "ghz");
            dimensions.SetCurrentUnit("SmallDistanceUnit", "m");
            dimensions.SetCurrentUnit("latitudeUnit", "deg");
            dimensions.SetCurrentUnit("longitudeunit", "deg");
            dimensions.SetCurrentUnit("DurationUnit", "HMS");
            dimensions.SetCurrentUnit("Temperature", "K");
            dimensions.SetCurrentUnit("SmallTimeUnit", "sec");
            dimensions.SetCurrentUnit("RatioUnit", "db");
            dimensions.SetCurrentUnit("rcsUnit", "dbsm");
            dimensions.SetCurrentUnit("DopplerVelocityUnit", "m/s");
            dimensions.SetCurrentUnit("Percent", "unitValue");
        }
        // 设置仿真时间
        // 参数:UTCG格式的字符串
        private void SetSimuTimes(string startTime, string stopTime, string epoch)
        {
            // 另外还可以通过接口方法 SetTimePeriod()设置
            IAgScenario scene = (IAgScenario)this.m_STKRoot.CurrentScenario;
            scene.StartTime = startTime;
            scene.StopTime = stopTime;
            scene.Epoch = epoch;
        }
        private void AddWaypoint(IAgVeWaypointsCollection waypoints, object Lat, object Lon, double Alt, double Speed, double tr)
        {
            IAgVeWaypointsElement elem = waypoints.Add();
            elem.Latitude = Lat;
            elem.Longitude = Lon;
            elem.Altitude = Alt;
            elem.Speed = Speed;
            elem.TurnRadius = tr;
        }

代码说明:

代码中的变量为m_STKApp和m_STKRoot类全局变量,定义如下:

    public class StkServ : System.Web.Services.WebService
    {
        public AgUiApplication m_STKApp { get; set; }
        public IAgStkObjectRoot m_STKRoot { get; set; }
        private string defualtScenName = "Demo";
……

STKProTutorial实例创建场景流程

  • 获取或创建STK应用(进程)实例;

  • 设置单位(建议每次初始化场景之前均根据实际情况需要设置各计算单位);

  • 设置仿真时间;

  • 通过Children.New()方法创建地面设施(Facility);

  • 从STK安装的本地数据库文件创建地面设施;

  • 创建目标(Target)对象,场景中被搜索的目标(Iceberg);

  • 创建Ship对象,利用大弧预报器(ePropagatorGreatArc)计算船只的运行轨迹。

  • 创建卫星(TDRS),演示了如何使用二体(运动)预报器(ePropagatorTwoBody)计算卫星的轨道。

  • 从STK的数据库文件加载创建卫星(TDRS_3),利用SGP4轨道预报器计算卫星轨道(代码中实际没有执行sgp4.Propagate()方法,但输出还是有卫星的轨道/轨迹,是因为从现有数据库文件加载的吗?还是默认会执行轨道计算?)。

  • 创建卫星(ERS1),这是场景中用于演示区域搜索的主卫星,使用J4摄动(二阶)预报器进行轨道预报(计算)。

  • 创建一个空间站(Shuttle),实际是一个卫星对象(eSatellite),利用J4预报器计算轨道,给空间站的二维显示设置了一个复杂的轮廓,在STK界面中可以看到效果,在输出的CZML文件中没有相关的显示数据。

  • 创建一个表示搜索区域的面目标(AreaTarget),并通过ePattern模式定义搜索区域。

  • 执行从卫星ERS1到搜索区域的Access计算。

  • 给卫星ERS1添加两个传感器,一个用于上行(固定指向),一个用于下行(追踪模式)。

  • 给地面站Wallops添加一个传感器(固定指向)。

  • 重新计算ERS1的轨道,计算周期为24h,并定制了不同时间段的显示特性。

  • 计算(卫星ERS1的)传感器Horizon对到地面设施Baikonur的Access数据。

最后执行Command命令输出CZML文件,代码如下(上述代码行524-526):

      //---------------------------------------------------------------
      // export CZML
      // 语法:ExportCZML <ScenarioPath> "<OutputFilePath>" {3D Model Server URL}
      // 要点:1)如果目标文件已经存在,则STK会覆盖现有文件;
      //       2)必须指定{3D Model Server URL},否则失败,可以在后续对czml文件执行处理替换
      filelocation = StkCalCommon.g_czmlLocalPath + "stkprotutorial.czml";
      command = "ExportCZML * \"" + filelocation + "\" " + @"http://assets.agi.com/models/";
      m_STKRoot.ExecuteCommand(command);
      //-----------------------------------------------------------------

经测试,必须要指定‘ExportCZML’命令的最后一个参数,即默认的3D模型的地址。生成czml文件后可根据应用实际配置替换默认的源。

生成的czml文件,大小约2.6M。

关于结果CZML文件

关于CZML文件的介绍,网络资源甚多,可自行搜索,本文仅列举一CSDN博文做参考。

Cesium学习之CZML的使用:https://blog.csdn.net/Gua_guagua/article/details/125024376

仿真时间

在代码中指定的仿真时间为4小时,代码如下:(注:包括SetSimuTimes方法)

            // 设置仿真时间:startTime, stopTime, epoch
            string startTime = "1 Jul 2002 00:00:00.00";
            string stopTime  = "1 Jul 2002 04:00:00.00";
            string epoch     = "1 Jul 2002 00:00:00.00";
            SetSimuTimes(startTime, stopTime, epoch);

        // 设置仿真时间
        // 参数:UTCG格式的字符串
        private void SetSimuTimes(string startTime, string stopTime, string epoch)
        {
            // 另外还可以通过接口方法 SetTimePeriod()设置
            IAgScenario scene = (IAgScenario)this.m_STKRoot.CurrentScenario;
            scene.StartTime = startTime;
            scene.StopTime = stopTime;
            scene.Epoch = epoch;
        }

CZML文件实际输出的仿真时间有4天多,参见czml文件输出第一个JSON元素的全局设置。

  {
    "id":"document",
    "name":"Demo",
    "version":"1.0",
    "clock":{
      "interval":"2002-07-01T00:00:00Z/2002-07-05T08:01:42.9280226419796Z",
      "currentTime":"2002-07-01T00:00:00Z",
      "multiplier":600,
      "range":"LOOP_STOP",
      "step":"SYSTEM_CLOCK_MULTIPLIER"
    }
  },
……

根据代码分析,输出结果CZML文件的仿真时间由场景中各对象中最大仿真时间输出为准,本例中的仿真时长由Ship对象(Cruise)决定。在场景中,为该对象设置了路径点,并利用大弧预报器计算了该对象的运行轨迹,该轨迹的运行时长决定了整个CZML输出的结束时间,这点可以从Cesium加载czml文件后的仿真运行效果可以直观看出,当Cruise运行到终点时,仿真显示从头开始循环。

参见上面代码第443行,卫星对象ERS1的仿真时间由此确定为24h(计算时长)。

关于传感器(Sensor)

在输出czml文件中,对传感器的描述中有“agi_conicSensor”属性,该属性是AGI专门为传感器在(Cesium)终端显示中定制的属性,网上查了相关资料,也有相关的包支持显示。本实践中经多方尝试,未果,不能在Web浏览器终端显示传感器的(扫描、定位、追踪等)效果。基本原因是因为版本不匹配,Cesium版本迭代更新较快,而为支持传感器显示的JS包已经有六年多没有更新了。

有成功的例子:https://blog.csdn.net/Gua_guagua/article/details/125024376

关于orientation属性

在输出czml文件中,有大量描述场景对象orientation属性的代码,该属性主要约束了对象三维模型显示时的动态指向(方向),如果仅是大颗粒度的场景演示,可以忽略该部分数据,由此可以大大缩减czml文件的大小。

关于对象的3D模型文件

在输出czml文件中搜索关键字‘gltf’或‘glb’,该关键字有关的属性指定了场景中对象的3D显示模型,在Cesium终端中鼠标双击对象会切换到该对象视点,并以三维模型显示该对象。搜索STK安装目录(或搜索本地缓存的网页文件)可以在本地找到相关的模型文件,通过在自己的Web服务目录中发布,可以无需访问AGI的官网实时在线获取文件。

客户端Cesium显示

假定IIS Web服务器地址为192.168.1.111

在本系列上一篇博文的基础上,增加如下代码。

      Sandcastle.addToolbarButtonWy("计算并加载CZML文件场景", function () {
        statusDisplay.innerHTML = "正在执行计算,请耐心等待……";
        $.ajax({
          type : "POST",
          contentType : "application/json; charset:utf-8",
          url : "http://192.168.1.111:8081/StkServ.asmx/CzmlExport",
          success : function (data) {
            // 注:直接返回的是ajax解析生成的JSON对象
            if(data["isSuccess"] == 0) {
              // 计算成功,加载结果czml文件
              viewer.dataSources.removeAll();
              viewer.dataSources.add(Cesium.CzmlDataSource.load(data["calResult"]));
              viewer.camera.flyHome(0);
              statusDisplay.innerHTML = "成功执行计算,结果CZML文件:" + data["calResult"];
            }
            else {
              statusDisplay.innerHTML = "计算失败,原因:" + data["description"];
            }
          },
          error : function(xhr) { // for test
            statusDisplay.innerHTML = xhr.responseText;
          }
        });
      });

第一次加载失败,查看服务器本地文件目录,czml文件已成功输出。经判断原因为客户端无法加载服务端的czml文件,需要在服务端手动添加IIS的MIME类型,即增加一条‘application/czml’,‘.czml’类型的文件。

IIS管理器中选择MIME类型。

点击‘MIME类型’后,点击右侧‘添加’。

在弹出窗口中输出如下,点击‘确定’按钮保存即可。

Cesium显示界面:

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值