Satellite: Difference between revisions
(→) |
(→) |
||
Line 790: | Line 790: | ||
<source lang="c#"> | <source lang="c#"> | ||
_map3DViewModel.ClickedItemEvent += clickedSatellite; | |||
private void clickedSatellite(object sender, ClickedItemEventArgs args) | |||
{ | |||
// Only react on right-click | |||
if (args.MouseButtonEventArgs.ChangedButton != MouseButton.Right) | |||
return; | |||
var itemId = args.ItemId; | |||
if (itemId.ListId == _satelliteLayer3D.PointListId) | |||
{ | |||
var satId = int.Parse(itemId.InstanceId); | |||
var sat = _satellites.GetSatellite(satId); | |||
sat.DrawLabels = !sat.DrawLabels; | |||
_satellites.UpdateSatellites(true); | |||
} | |||
} | |||
</source> | </source> | ||
[[Category:Satellite]] | [[Category:Satellite]] |
Revision as of 10:09, 26 January 2022
The GDK layer "Satellite" defined in the namespace TPG.GeoFramework.SatelliteLayer handles satellites which therefore easily can be separated from other draw types. Satellite objects in this layer are controlled through the TPG.GeoFramework.SatelliteLayer.SatelliteLayerViewModel class.
The TLE string
A satellite position and its trajectory is computed from a TLE string. The format of the TLE is described in https://www.space-track.org/documentation#/tle. The TLE for a satellite can for example be acquired from www.space-track.org using the TPG.Satellite.Implementation.SatelliteUtil.GetSpaceTrack method. The content of the TLE will be unique for each satellite and is also adjusted through time to give as accurate position computations as possible.
//Example of TLE text used in tests
public static string TestTle()
{
var tle = "0 ISS (ZARYA)";
tle += "\n1 25544U 98067A 20227.03014521 .00002523 00000-0 53627-4 0 9998";
tle += "\n2 25544 51.6461 62.1977 0001435 30.4470 104.2862 15.49164713240985";
return tle;
}
Convert time to satellite position
The mathematics to convert time to position based on the TLE is done with an external library. Currently the conversion is supported by two different libraries. The functionality is available through the facades TPG.Data.Satellite.Interface.ISatelliteData and TPG.Data.Satellite.Interface.ISatelliteTle. This is done so that adding new libraries can be done without affecting the satellite code.
The creation process for a satellite object, based on given TLE and time, is not available for the end user. This process will automatically select one of the implemented libraries, but it is possible to select a specific library:
//create satellite without selecting library
var satellite1 = new TPG.GeoFramework.SatelliteLayer.Satellite();
//select the first implemented library. Usually this is done through UI
var tle = new SatelliteTle();
var satellite2 = new TPG.GeoFramework.SatelliteLayer.Satellite(tle.Libraries.First());
Implemented TLE libraries
These libraries are free licenses.
The OneSgp4GdkTle library
https://www.nuget.org/packages/One_Sgp4 https://github.com/1manprojects/one_Sgp4
The SgpDotNeTle library
https://www.nuget.org/packages/SGP.NET/ https://github.com/parzivail/SGP.NET
Other possible libraries
The library http://www.zeptomoby.com/satellites/ might be of interest in the future. This library has a very extensive functionality, but has not been added since it has a very high license cost.
What is a satellite?
A satellite object consists of two draw objects; a satellite position object of type symbol and a satellite trajectory object of type line. Both object types are not required, but are usually used. The position shows where the actual satellite is at the given time. The trajectory shows where the satellite has been between a given start and stop time.
Useful internet resources:
https://in-the-sky.org/satmap_worldmap.php
https://www.phiphase.com/wp-content/uploads/2021/10/satellite_coverage-1.pdf
Satellite implementation
The Satellite class implements dedicated interfaces for different satellite functionality.
public class Satellite : ISatellite, ISatelliteTime, ISatelliteDevice, ISatelliteFootprints, ISatelliteLineOfSight, ISatelliteTimeTicks
{
.
.
}
ISatellite class
Properties for basic satellite functionality like position, trajectory, satellite ID, satellite name, visibility etc. The properties for the position and the trajectory are:
ISatellite.ISatellitePosition
The satellite position is handled by the ISatellitePosition class. The property ISatellitePosition.Position contains the (Lat,Lon) position, corresponding time for the position, satellite height at the position, and also the possibility to add additional key,value information for this point.
ISatellite.ISatelliteTrajectory
The satellite trajectory is handled by the ISatelliteTrajectory class. The property ISatelliteTrajectory.Trajectory contains a list of satellite positions which is used to draw the trajectory line. The parameters StartTime and StopTime contains the start and stop time for the trajectory.
ISatelliteTime class
Properties used in time related methods for the satellite object.
ISatelliteDevice class
Properties used in device related computations for satellite object. The most important computation is adjusting the trajectory's start and stop time so that the trajectory covers the actual device extent. This class is normally not used externally.
ISatelliteFootprints class
Adds footprint functionality to a satellite. A satellite can have many footprints with different setup where each footprint has a unique ID.
Example of satellite with several footprints:
This code adds a footprint to a satellite:
var satellite = satelliteLayerViewModel.GetSatellite(satelliteId) as Satellite;
satellite.AddFootprint(new SatelliteFootprint()
{
Id = Guid.NewGuid().ToString(),
ViewAngle = Footprint,
CircleLineColor = LineColor,
HubLineColor = OutlineColor != Colors.Black ? OutlineColor : LineColor,
HubLineThickness = OutlineColor != Colors.Black && TimerSecs < 50 ? TimerSecs : new SatelliteFootprint().HubLineThickness,
CircleLineThickness = LineWidthValue,
FootprintSettings = FootprintSetting(),
LabelPlacement = FootprintSetting(),
TiltAngle = FootprintTiltAngle,
RotateAngle = FootprintRotateAngle,
DrawFootprintAsStripe = FootprintAsStripe,
AccumulateFootprintStripe = AccumulateFootprintStripe,
StripeFillColor = stripeFillColor,
StripeLineColor = LineColor,
StripeFillStyle = FillStyle.Solid
});
In addition the class contains methods to handle footprints like retrieve and remove footprints.
ISatelliteFootprint class
Defines a footprint. The properties ViewAngle, TiltAngle and RotateAngle defines the size and position of the footprint. These properties are used to calculate the dimensions of the footprint with the Satellite.CalculateFootprintDimensions method. Actual geo-points for the footprint is then calculated with SatelliteFootprintRenderer.CreateFootprintPoints.
ISatelliteLineOfSight class
This class has functionality to check if a satellite is visible from a given observation point.
ISatelliteTimeTicks class
This add time ticks to the trajectory rendering. It is implemented by the Satellite class so that these settings can be handled before an actual trajectory is added to the satellite object. It is also implemented by the SatelliteLayerViewModel class, ticks values set on the viewmodel will be used on all satellites which have no set tick values.
How to create satellite objects
The satellite objects are first created and then added to the satellite layer.
Create only a satellite position object
This test code shows how to create a position object:
var satellite = new TPG.GeoFramework.SatelliteLayer.Satellite
{
Tle = TestTle(),
MetadataSetting = (long)SatelliteObject.TleMetadata.All,
RecalculateTrajectoryAlways = FullCurveCalculations
};
Assert.NotNull(satellite);
//calculate the satellite position for this time
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
Assert.IsTrue(satellite.CreateSatellite(time));
Assert.AreEqual(1, satellite.Objects.Count);
Assert.IsNotNull(satellite.Objects.First() as ISatellitePosition);
Create a satellite with both position and trajectory objects
This test code shows how to create a complete satellite:
var satellite = new TPG.GeoFramework.SatelliteLayer.Satellite
{
Tle = TestTle(),
MetadataSetting = (long)SatelliteObject.TleMetadata.All,
RecalculateTrajectoryAlways = FullCurveCalculations
};
Assert.NotNull(satellite);
//calculate the satellite position for this time
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
//create trajectory 10 minutes before and after position
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
Assert.AreEqual(2, satellite.Objects.Count);
Assert.NotNull(satellite.SatellitePosition);
Assert.NotNull(satellite.SatelliteTrajectory);
Add satellites to the satellite layer
This code shows how to add satellites to the satellite layer:
//The satellite layer
SatelliteLayerViewModel _satelliteLayerViewModel = ...
//create satellites, details not shown
var createdSatellites = new List<ISatellite>();
createdSatellites = CreateSatellites();
//prevent refresh while we are adding satellites
_satelliteLayerViewModel.LockRedraw = true;
foreach (var satellite in createdSatellites)
_satelliteLayerViewModel.AddSatellite(satellite);
//will refresh rendering
_satelliteLayerViewModel.LockRedraw = false;
Automatic update of satellite positions
Automatic update of satellite positions is implemented in the SatelliteLayerViewModel (2D layer). The viewmodel handles the actual conversion of time to satellite positions, but the time updating is extracted to a separate class.
SatelliteSystemTimeUpdate
This class updates the satellite system time.
ISatellitesUpdate
This interface must be implemented by the viewmodel so that the SatelliteSystemTimeUpdate object can update the satellite positions.
Code example for automatic satellite position updating
This code shows the major details of how the SatelliteLayerViewModel handles the automatic updating of satellite positions.
public partial class SatelliteLayerViewModel : GeoLayerViewModel, ISatelliteLayerViewModel, ISatellitesUpdate, .....
{
private readonly SatelliteSystemTimeUpdate _satellitesSystemTimeUpdate;
public SatelliteLayerViewModel(.....)
{
...
//this object handles the satellite system time; either actual time or starting from a given base time for example yesterday 13.00. In addition the time updating can be increased or decreased,
//or even reversed using the playback property.
_satellitesSystemTimeUpdate = new SatelliteSystemTimeUpdate(this);
}
//The satellite system time is available externally, but is actual implemented by the SatelliteSystemTimeUpdate class.
public DateTime Time
{
get => _satellitesSystemTimeUpdate.Time;
set => _satellitesSystemTimeUpdate.Time = value;
}
//If null, the system time will be current time (DateTime.Now). If set, the given time is the start time when the automatic update is turned on.
public DateTime? SatelliteBaseTime
{
get => _satellitesSystemTimeUpdate.SatelliteBaseTime;
set => _satellitesSystemTimeUpdate.SatelliteBaseTime = value;
}
//If 1.0, the automatic time update is real time.
//If different than 1.0, the time update is equal to the playback factor.
//if negative, the time update is reversed meaning that the satellites are moving backward in time.
public double PlaybackSpeed
{
get => _satellitesSystemTimeUpdate.PlaybackSpeed;
set => _satellitesSystemTimeUpdate.PlaybackSpeed = value;
}
//starts the automatic updating of the satellite system time.
public bool StartAutomaticSatellitePositioning(double updateFrequency)
{
updateFrequency = updateFrequency < 0.3 ? 0.3 : updateFrequency;
_satellitesSystemTimeUpdate.ResetAutomaticTimerBaseTime();
_satellitesSystemTimeUpdate.SetAndStartTimerTime(updateFrequency * 1000.0, SatelliteLayerSettings, GeoContext);
IsAutomaticPositioning = true;
return true;
}
//stops the automatic updating of system time
public bool StopAutomaticSatellitePositioning()
{
_satellitesSystemTimeUpdate.Stop();
IsAutomaticPositioning = false;
return true;
}
//This method is called by _satellitesSystemTimeUpdate on each system time update
public void UpdateSatelliteTimes()
{
UpdateSatellites(true, true);
}
//do the actual update of all satellites.
//This code only shows the necessary code for updating the satellite positions.
private void UpdateSatellites(bool symbol, bool trajectory)
{
//make sure that this is the only update instance
lock (_lockUpdateSatellite)
{
//prevent any rendering while updating
LockRedraw = true;
var satellites = GetSatellites();
foreach (var satellite in satellites.Values.OfType<Satellite>())
{
if (symbol)
satellite.UpdateSatellite(Time);
if (!trajectory) continue;
satellite.UpdateSatellite(Time,
satellite.TrajectoryBackwardSpan.Ticks > 0
? satellite.TrajectoryBackwardSpan
: TrajectoryBackwardSpan,
satellite.TrajectoryForwardSpan.Ticks > 0
? satellite.TrajectoryForwardSpan
: TrajectoryForwardSpan);
}
LockRedraw = false;
}
}
//GeoLayerViewModel override
public override void Update()
{
...
//Update timer frequency based on context scale. For performance, the frequency is increased with lower context scale since fewer satellites then will visible. The positions for all satellites
//will be updated since this can be processed without increasing the CPU, but only visible satellites are actually rendered. Rendering many satellites will affect the CPU.
if (ViewportExtentChanged && IsAutomaticPositioning)
_satellitesSystemTimeUpdate.SetAndStartTimerTime(SatelliteLayerSettings, GeoContext);
...
}
}
Historic satellites
A satellite's TLE is updated over time since the trajectory changes slightly for each turn. It might therefore be of interest to render the different trajectories and positions for a satellite.The historic instances of the satellite will all have the same satellite ID so this property can not be used directly to differentiate between the current satellite object and the historic satellite objects. Instead the historic SatelliteId is set to the original satellite's ID plus an historic offset and an index number equal to number of historic satellites.
ISatelliteHistory class
Adds necessary functionality to differentiate between regular satellite and historic satellites.
public interface ISatelliteHistory : ISatellite
{
/// <summary>
/// Id for historic satellite.
/// </summary>
new int SatelliteId { get; set; }
/// <summary>
/// The offset added to all historic satellites
/// </summary>
int HistoryOffset { get; }
/// <summary>
/// The index for historic satellite. There can be many historic satellites for one true satellite id.
/// </summary>
int HistoryIndex { get; }
/// <summary>
/// Returns false since this is not a regular satellite.
/// </summary>
new bool IsSatelliteClass { get; }
/// <summary>
/// Returns the non-historic SatelliteId
/// </summary>
int RegularSatelliteId { get; }
}
The IsSatelliteClass will be false for historic satellites and true for regular satellite. For an historic satellite SatelliteId = RegularSatelliteId + HistoryOffset + HistoryIndex. This value can easily be retrieved by RegularSatelliteId.
Create a historic satellite object
The historic satellite is created from the satellite's TLE at a particular time. First create a regular satellite from this TLE and then create the final historic satellite based on the regular satellite.
This test code creates a stand alone historic satellite:
var satellite = new GeoFramework.SatelliteLayer.Satellite();
...
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
satellite.Tle = TestTle();
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
//if this is the first historic satellite with this ID, then historic index is 1
var historySatellite = new SatelliteHistory(satellite, 1);
Assert.IsTrue(satellite.IsSatelliteClass);
Assert.IsFalse(historySatellite.IsSatelliteClass);
This test code is more realistic since it adds the historic satellite to the viewmodel:
var satellite = new GeoFramework.SatelliteLayer.Satellite();
...
var time = new DateTime(2020, 08, 14, 7, 55, 26, DateTimeKind.Utc);
satellite.Tle = TestTle();
var span = new TimeSpan(0, 0, 10, 0);
Assert.IsTrue(satellite.CreateSatellite(time, span, span));
//adds the satellite as a historic satellite
viewmodel.AddSatelliteHistory(satellite, satellite.SatelliteTle.Epoch, true, false);
//add even simpler
viewmodel.AddSatelliteHistory(satellite, true, false);
Assert.AreEqual(1, viewmodel.SatelliteHistories.Count);
Assert.AreEqual(satellite.SatelliteId, viewmodel.SatelliteHistories[0]);
var history = viewmodel.SatelliteHistory(satellite.SatelliteId);
Assert.AreEqual(2, history.Count);
How to analyze the satellites visibility
The analyze functionality is based on observation (line-of-sight) points which tracks one or many satellites. The tracked satellites can be included or excluded from the analyze process. The results from the process can either be used directly or exported to a xml format.
Observation points in the satellite layer
The ISatelliteLineOfSight adds line-of-sight functionality like adding and removing observation points to the satellite layer.
This code adds an observation point to the satellite layer. The point will track the given satellite.
var satellite = CreateSatellite(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
LineOfSightEnabled
Enables or disables all line-of-sight functionality.
LineOfSightViewPoint
Returns the layer's specific line-of-sight point or the line-of-sight point(s) for a specific satellite.
SatelliteInSight
Checks if a specific satellite is tracked by any line-of-sight points or if a specific line-of-sight point tracks a specific satellite.
Defining an observation point
The ISatelliteLineOfSightViewPoint class defines the observation point. The points are handled by the satellite layer.
ViewPointId
A unique value for the view point.
Position
The ground position for the view point.
IncludeInComputation
Controls if the view point is included in the analyze process.
TrackSatellite
Functionality to control which satellites are included in the view point's analyze process.
ComputeSettings
Controls how the analyze process creates visibility results around the view point.
Setting up the analyze process
The class ISatelliteLineOfSightComputeSettings controls the analyze process for a view point. The process can use one or many points around the view point so that a visibility pattern can be created for the area around the view point. In addition the process can analyze the visibility for one or many timestamps so the visibility over time can be analyzed.
This code shows how to analyze specific points around the view point
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(50, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 120));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
This code shows how to analyze many points in the area around the view point
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
//add three timestamps that shall be analyzed
point.ComputeSettings.Times.Clear();
point.ComputeSettings.Times.Add(time);
point.ComputeSettings.Times.Add(time.AddMinutes(-1));
point.ComputeSettings.Times.Add(time.AddMinutes(1));
point.ComputeSettings.StepCount = 10; // will create 20 x 20 points
point.ComputeSettings.StepRange = 1000; //mtrs between each point
//make sure there are not any manual points
satelliteLineOfSightViewPoint.ComputeSettings.ComputePoints = null;
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
How to analyze visibility around observation point
The method ISatelliteLineOfSight.ComputeInSight starts the asynchronous process of checking the visibility of a satellite from the observation point.
This test code starts the visibility process:
....
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
_processed = false;
viewmodel.ViewPointsProcessed += ViewmodelOnLineOfSightProcessed;
viewmodel.ComputeInSight(true);
Thread.Sleep(2000);
Assert.IsTrue(_processed);
viewmodel.ViewPointsProcessed -= ViewmodelOnLineOfSightProcessed;
Assert.IsTrue(viewmodel.HasSatelliteVisibleComputations);
var results = viewmodel.LineOfSightViewPoint(id);
Assert.NotNull(results);
Assert.NotNull(results.Status);
var values = results.Status.Values.ToList();
....
}
private void ViewmodelOnLineOfSightProcessed(object sender, LineOfSightProcessedEventArgs args)
{
_processed = true;
}
Example of in sight analyze process:
When are satellites in the area
The method ISatelliteLineOfSight.ComputeWhenInArea starts the asynchronous process of checking when the satellite is visible from the observation point.
This test code starts the in-area process:
....
var time = new DateTime(2021, 9, 30, 15, 0, 0);
var satellite = CreateSatellite1(time);
viewmodel.AddSatellite(satellite);
var groundPoint = new GeoUnits.GeoPos(61, 100);
var id = Guid.NewGuid().ToString();
var point = new SatelliteLineOfSightViewPoint(id)
{
ViewPointName = "nr1",
Position = groundPoint,
IncludeInComputation = true
};
point.ComputeSettings.ComputePoints = new List<GeoPos>();
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(70, 90));
point.ComputeSettings.ComputePoints.Add(new GeoUnits.GeoPos(60, 90));
point.ComputeSettings.Times.Add(time);
point.TrackSatellite(satellite.SatelliteId);
Assert.IsTrue(viewmodel.AddLineOfSightViewPoint(point));
_processed = false;
viewmodel.ViewPointsProcessed += ViewmodelOnLineOfSightProcessed;
viewmodel.ComputeWhenInArea(time.AddMinutes(-30), time.AddMinutes(30), new TimeSpan(0, 0, 0, 10), true);
Thread.Sleep(2000);
Assert.IsTrue(_processed);
viewmodel.ViewPointsProcessed -= ViewmodelOnLineOfSightProcessed;
Assert.IsTrue(viewmodel.HasSatelliteVisibleComputations);
var results = viewmodel.LineOfSightViewPoint(id);
Assert.NotNull(results);
Assert.NotNull(results.Status);
var status = results.Status.First();
var timeComputations = status.Value.SatelliteComputations.TimeComputations;
Assert.NotNull(timeComputations);
....
}
private void ViewmodelOnLineOfSightProcessed(object sender, LineOfSightProcessedEventArgs args)
{
_processed = true;
}
Check if observation points are in any satellite's footprint
The method ISatelliteLineOfSight.ComputeInFootprints starts the asynchronous process of checking if an observation point is within any footprints.
This test code shows how to start the footprint process
....
viewmodel.ComputeInFootprints(true);
...
Assert.IsTrue(viewmodel.HasInsideFootprintComputations);
var results = viewmodel.LineOfSightViewPoint(id);
var status = results.Status.First();
var computations = status.Value.FootprintComputations.Computations;
....
When are satellites inside footprints
The method ISatelliteLineOfSight.ComputeWhenInFootprints starts the asynchronous process of checking when the satellite is inside a footprint.
This test code shows how to start the footprint process
....
viewmodel.ComputeWhenInFootprints(startTime, stopTime, steptTime, false);
...
Assert.IsTrue(viewmodel.HasInsideFootprintComputations);
var results = viewmodel.LineOfSightViewPoint(id);
var status = results.Status.First();
var computations = status.Value.FootprintComputations.Computations;
....
Analyze results as XML
The method ISatelliteLineOfSight.StatusAsXml returns the analyze results as XML. The XML content is dependent of type of analyze process.
This test code shows how to get the results as XML
....
viewmodel.ComputeInSight(true);
...
var xml = viewmodel.StatusAsXml();
....
EXample of analyze process as XML:
Implement satellite functionality in GDK layer
The satellites can easily be integrated in a GDK layer since the handling of the satellites are extracted into a dedicated classes. Automatic updating of all satellites is also extracted from the GDK layer.
Handling satellite objects
The satellites are handled by a dedicated store object that can be shared between several layers, for example 2D and 3D.
The satellite store object
The store object can either be created by the viewmodel or created separately and then passed into the viewmodel through the constructor. SatellitesStore implements the following interfaces: ISatellitesStore, ISatelliteHistories and ISatelliteListeners.
Satellite system time
The system time is extracted into a dedicated class, SatelliteSystemTimeUpdate, which is used by the store object.
Connecting viewmodel and satellite store
Automatical update of satellite time and thereby positions are handled by SatellitesStore and SatelliteSystemTimeUpdate, but the viewmodel might need to be informed about the position changes.
The listener class
The listener class, ISatelliteListeners, handles which objects that shall be informed about position changes.
Position changes information
The class, ISatellitesUpdate, is used to relay information about position changes.
Connecting everything
The following extracted code shows how everything is connected in the SatelliteLayerViewModel.
public partial class SatelliteLayerViewModel : ... ISatelliteLayerViewModel, ISatellitesUpdate, ...
{
private readonly SatellitesStore _satellitesStore;
//constructor without store object, layer uses its own store object
public SatelliteLayerViewModel(IGeoUnitsSetting geoUnitsSetting, IGeoContext geoContext, IGeoNavigator geoNavigator, SatelliteLayerViewFactory satelliteObjectLayerViewFactory) ....
{
...
_satellitesStore = new SatellitesStore();
_satellitesStore.AddListener(this);
}
//store object is already created, use this object
public SatelliteLayerViewModel(ISatellitesStore satelliteStore, IGeoUnitsSetting geoUnitsSetting, ....
{
...
_satellitesStore = (SatellitesStore) satelliteStore;
_satellitesStore.AddListener(this);
}
//starts automatic updating
public bool StartAutomaticSatellitePositioning(double updateFrequency)
{
updateFrequency = updateFrequency < 0.3 ? 0.3 : updateFrequency;
_satellitesStore.SatelliteSystemTimeUpdate.ResetAutomaticTimerBaseTime();
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(updateFrequency * 1000.0, SatelliteLayerSettings, GeoContext);
IsAutomaticPositioning = true;
}
//stop updating
public bool StopAutomaticSatellitePositioning()
{
_satellitesStore.SatelliteSystemTimeUpdate.Stop();
IsAutomaticPositioning = false;
}
public override void Update()
{
...
//must update frequency if scale is changed
if (ViewportExtentChanged && IsAutomaticPositioning )
{
_satellitesStore.SatelliteSystemTimeUpdate.SetAndStartTimerTime(SatelliteLayerSettings, GeoContext);
}
...
}
//ISatellitesUpdate implementations
//callback before satellites are automatically updated
public void BeforeSatellitesUpdate()
{
LockRedraw = true;
}
//callback after satellites are automatically updated
public void AfterSatellitesUpdate()
{
LockRedraw = false;
var trackedSatellite = _satellitesStore.TrackedSatellite(TrackSatellite);
if (trackedSatellite != null)
CenterToPosition(trackedSatellite as Satellite);
}
}
Satellites in 3D
Satellites can also be displayed in 3D. This is handled by the class TPG.Globe3D.SatelliteLayer. When using 2D and 3D in the same application you should make a SatellitesStore outside the layers and inject it in both 2D and 3D layers.
Initializing the 3D satellite layer
The following code example sets up both 2D and 3D satellite layers from the same satellite store.
// Common satellites store
_satellites = new SatellitesStore();
// 2D layer
var satelliteLayerViewFactory = new SatelliteLayerViewFactory(_drawObjectLayerFactory.SymbolsStore);
_satelliteLayerViewModel = new SatelliteLayerViewModel(_satellites, _geoUnitsSetting, geoContext, geoNavigator, satelliteLayerViewFactory);
_mapViewModel.Layers.Add(_satelliteLayerViewModel);
// 3D layer
_satelliteLayer3D = new SatelliteLayer(_map3DViewModel, _satellites, drawObjectDataFactory, stylingXml);
Adding and updating satellites
The 3D layer will display all visible ISatellite objects in the SatellitesStore with the same styling parameters as are used in 2D. The 3D graphics will be updated upon each Update() call in the SatellitesStore, so if you for example control this through an ISatelliteSystemTimeUpdate interface, the satellites will be continuously updated.
If you change any of the styling or other visible parameters of the satellites, you may need to update explicitly, either through a call to SatellitesStore.Update(true) or by calling SatelliteLayer.UpdateSatellites() directly.
Interacting with satellites in 3D
The satellite icons are 3D items of the same type as tracks and point symbols, and you may use the IGlobe3DViewModel.ClickedItem event to respond to mouse clicks on these. Each satellite item will have an ItemId consisting of the SatelliteLayer point list id (accessible through ISatelliteLayer.PointListId) and the satellite ID as InstanceID.
The following example illustrates how you can toggle the 3D label on a clicked satellite:
_map3DViewModel.ClickedItemEvent += clickedSatellite;
private void clickedSatellite(object sender, ClickedItemEventArgs args)
{
// Only react on right-click
if (args.MouseButtonEventArgs.ChangedButton != MouseButton.Right)
return;
var itemId = args.ItemId;
if (itemId.ListId == _satelliteLayer3D.PointListId)
{
var satId = int.Parse(itemId.InstanceId);
var sat = _satellites.GetSatellite(satId);
sat.DrawLabels = !sat.DrawLabels;
_satellites.UpdateSatellites(true);
}
}