Maria globe client: Difference between revisions

From Maria GDK Wiki
Jump to navigation Jump to search
()
()
Line 464: Line 464:
In this section, we will add and update different draw objects, and see how they are presented in the 2D and 3D modes.
In this section, we will add and update different draw objects, and see how they are presented in the 2D and 3D modes.


==== Adding draw object layer ====
==== Adding draw object layer and draw object view model ====


To be able to display draw object, we need to include a draw object layer in the globe client.  
To be able to display draw object, we need to include a draw object layer in the globe client.  


In the '''''MainViewModel''''', add a draw object layer field, and in the constructor, add creation of the layer and create a '''''DrawObjectViewModel''''' object (described below)  - and add the layer to the '''''Layers''''' list.
First, add a view model class, '''''DrawObjectViewModel''''', to handle the draw object interaction:


<source lang="C#">
* Inheriting '''''ViewModelBase'''''  
...
* Add a constructor taking a draw layer object as input
private DrawObjectLayer _drawObjectLayer;
...
    _drawObjectLayer = new DrawObjectLayer(GlobeMapManager, DrawObjectLayerFactory, true)
    {
        InitializeCreationWorkflows = true
    };
 
    DrawObjectViewModel = new DrawObjectViewModel(_drawObjectLayer);
 
    Layers.Add(_drawObjectLayer);
...
</source>
 
==== Draw object view model ====
 
To handle the draw object interaction, add a view model class, '''''DrawObjectViewModel''''' with:
* inheritance of '''''ViewModelBase'''''  
* a constructor taking a draw layer object as input
* Create and implement event handlers for:
* Create and implement event handlers for:
** '''''LayerInitialized''''' -
** '''''LayerInitialized'''''  
** '''''ServiceConnected'''''
** '''''ServiceConnected'''''
** '''''ExtendedDrawObjectLayer.ActiveCreationWorkflowCompleted'''''


<source lang="C#">
<source lang="C#">
...
public class DrawObjectViewModel : ViewModelBase
public class DrawObjectViewModel : ViewModelBase
{
{
     private IMariaDrawObjectLayer _drawObjectLayer;
     private IMariaDrawObjectLayer _drawObjectLayer;
    private IMariaService _drawObjectService;


     public DrawObjectViewModel(DrawObjectLayer drawObjectLayer)
     public DrawObjectViewModel(DrawObjectLayer drawObjectLayer)
Line 506: Line 487:
         _drawObjectLayer.LayerInitialized += DrawObjectLayer_LayerInitialized;
         _drawObjectLayer.LayerInitialized += DrawObjectLayer_LayerInitialized;
         _drawObjectLayer.ServiceConnected += DrawObjectLayer_ServiceConnected;
         _drawObjectLayer.ServiceConnected += DrawObjectLayer_ServiceConnected;
     }  
    }
 
    private void DrawObjectLayer_LayerInitialized()
    {
        _drawObjectService = new MariaService(
            "DrawObjectService",
            "http://localhost:9008/drawobjects",
            BindingType.BasicHttp);
 
        _drawObjectLayer.DrawObjectServices = new ObservableCollection<IMariaService> { _drawObjectService };
 
        _drawObjectLayer.CreateDrawObjectList("local", true);
    }
 
    private void DrawObjectLayer_ServiceConnected(object sender, MariaServiceEventArgs args)
    {
        _drawObjectLayer.ActiveDrawObjectService = _drawObjectLayer.DrawObjectServices[0];
 
        _drawObjectLayer.CreateDrawObjectList("test", false);
        _drawObjectLayer.ActiveDrawObjectList = "test";
     }
}
}
...
</source>
</source>


In the '''''MainViewModel''''', add a draw object layer field, and in the constructor, add creation of the layer and '''''DrawObjectViewModel'''''  and add the layer to the '''''Layers''''' list.
<source lang="C#">
...
public DrawObjectViewModel DrawObjectViewModel { get; private set; }


public MainViewModel()
{
    ...
    DrawObjectLayerFactory = new DrawObjectLayerFactory();
    _drawObjectLayer = new DrawObjectLayer(GlobeMapManager, DrawObjectLayerFactory, true)
        { InitializeCreationWorkflows = true };
    DrawObjectViewModel = new DrawObjectViewModel(_drawObjectLayer);
    Layers.Add(_drawObjectLayer);
}
...
</source>


==== Draw object utilities ====
==== Draw object utilities ====
Line 539: Line 555:
...
...
</source>
</source>


Implement command handlers for the draw object utilities in the '''''MainViewModel''''':
Implement command handlers for the draw object utilities in the '''''MainViewModel''''':
Line 548: Line 560:
<source lang="C#">
<source lang="C#">
...
...
public DrawObjectViewModel DrawObjectViewModel { get; private set; }
public ICommand VolumeTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateVolumeObject); } }
public ICommand VolumeTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateVolumeObject); } }
public ICommand LineTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateLineObject); } }
public ICommand LineTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateLineObject); } }
Line 560: Line 570:
<source lang="C#">
<source lang="C#">
...
...
private IMariaDrawObjectLayer _drawObjectLayer;
public void CreateOrUpdatePointObject(object obj)
public void CreateOrUpdatePointObject(object obj)
{
{

Revision as of 11:57, 17 September 2019

This page describes how to create a Maria GDK map client utilising MariaGlobeMapControl with 2D and 3D visualisation of map, tracks and draw objects.

Maria Globe Client

General

This page is under construction!
  • You will need to include the following NuGet package:
    • TPG.Maria.MariaGlobeMapControl (Currently available Teleplan Globe internal only)
  • Sample code is found in the MariaGlobeClient project, in the Sample Projects solution.
    • Note that the sample code is specifying the service connections in code, and not by app.config.

Utilising the globe control

Including MariaGlobeMapControl

Create a WPF Application, and add reference to the TPG.Maria.MariaGlobeMapControl package.
For NuGet details, see Maria Basic Map Client.

Add the MariaGlobeMapControl to the Main window xaml.

<Window x:Class="MariaGlobeClient.MainWindow"
        . . .
        Title="MariaGlobeClient" Height="600" Width="600">
    <Grid>
        <MariaGlobeMapControl x:Name="MariaGlobeCtrl" Background="#E9ECFA"                      
                     Layers="{Binding Layers}"
                     MiniMapLayer="{Binding MapViewModel.MiniMapLayer, Mode=OneWay}"
                     CenterScale="{Binding MapViewModel.CenterScale}" 
                     CenterPosition="{Binding MapViewModel.CenterPosition}"
                     MouseMoveDistanceToStartTracking="0"
                     ZoomOnDblClick="False"
                     DegreeToLockRotateAndScale="2"
                     PercentageToLockScaleOnly="2.0"
                     Is3DMode="False"
                     IsRulerVisible="True" 
                     IsCenterPositionIndicatorEnabled="True"
                     IsMiniMapVisible="True" />
    </Grid>
</Window>

Add view models

  • Add a view model class (MainViewModel) for communication with the Maria component, and another (MapViewModel) for map handling. Both including inheritance of ViewModelBase, for details see Prepare your application for interactions.
  • Set the data context of your client window to the MainViewModel, and implement an event handler for the WindowClosing event. For details see Maria Basic Map Client.

MainViewModel

Extend the inheritances to include the IMariaGlobeMapViewModel and implement the interface.

public class MainViewModel : ViewModelBase, IMariaGlobeMapViewModel
{
    public IGlobeMapManager GlobeMapManager { get; }
    public IDrawObjectLayerFactory DrawObjectLayerFactory { get; private set; }
    public IGlobeMapViewModel GlobeMapViewModel { get; set; }
...

Add the following fields and properties:

. . .
    private IMariaMapLayer _mapLayer;
    private IMariaMapLayer _miniMapLayer;

    public MapViewModel MapViewModel { get; }
    public ObservableCollection<IMariaLayer> Layers { get; set; }
. . .

In the constructor:

  • initialise the Layer property
  • set up the map services
  • create the GlobeMapManager
  • create map layer, mini-map layer and add to the Layers property
  • instanciate the MapViewModel


...
public MainViewModel()
{
    Layers = new ObservableCollection<IMariaLayer>();

    IBindingFactory bindingFactory = new BindingFactory();
    IEndpointAddressFactory endpointAddressFactory = new EndpointAddressFactory();

    // Set up template client
    var mapTemplateServiceClient = new MapTemplateServiceClientFactory(
        bindingFactory,
        endpointAddressFactory).New("http://tpg-mariagdktest:9008/maptemplates", BindingType.BasicHttp);

    // Set up catalog service client and connect to service
    var mapCatalogServiceClient = new MapCatalogServiceClient(
        bindingFactory.New(BindingType.BasicHttp),
        endpointAddressFactory.New("http://tpg-mariagdktest:9008/catalog"));

    GlobeMapManager = new GlobeMapManager(mapCatalogServiceClient, mapTemplateServiceClient);
    GlobeMapManager.TileCacheManager.MaxCacheSize = 250;

    _mapLayer = new MapLayer(GlobeMapManager);
    _miniMapLayer = new MapLayer(GlobeMapManager);
    Layers.Add(_mapLayer);

    MapViewModel = new MapViewModel(_mapLayer, _miniMapLayer);
}

Make the class disposable by including and implementing IDisposable.

...
 public class MainViewModel : ViewModelBase, IMariaGlobeMapViewModel, IDisposable
{
...
    public void Dispose()
    {
        _mapLayer?.Dispose();
        _miniMapLayer?.Dispose();
        GlobeMapViewModel?.Dispose();
    }

MapViewModel

Add the following fields and properties:

...
    private readonly IMariaMapLayer _mapLayer;

    public IMariaMapLayer MiniMapLayer { get; private set; }
    public GeoPos CenterPosition { get; set; }      
    public double CenterScale { get; set; }
...

Create a constructor, taking map layer and and mini-map layer as parameters and initialising map and mini-map event handling.

public MapViewModel(IMariaMapLayer mapLayer, IMariaMapLayer miniMapLayer)
{
    _mapLayer = mapLayer;

    if (_mapLayer != null)
        _mapLayer.LayerInitialized += OnMapLayerInitialized;

    MiniMapLayer = miniMapLayer;
    if (MiniMapLayer != null)
        MiniMapLayer.LayerInitialized += OnMiniMapLayerInitialized;
}

Implement the LayerInitialized event handlers for the map and mini map.

...
    private void OnMapLayerInitialized()
    {
         CenterPosition = new GeoPos(59.908358, 10.628190); // TPG - Lysaker
         CenterScale = 500000;

        _mapLayer.ActiveMapTemplate = _mapLayer.ActiveMapTemplates.Any() ? _mapLayer.ActiveMapTemplates.First() : null;
    }

    private void OnMiniMapLayerInitialized()
    {
        MiniMapLayer.ActiveMapTemplate = _mapLayer.ActiveMapTemplates.Any() ? _mapLayer.ActiveMapTemplates.First() : null;
    }
...

Running the globe client

Running your application, the window area should now include 2D map information and mini map - and you should be able to navigate the map!

Globe client with 2D map

To start the globe client in #D mode, Change the Is3DMode property of the MariaGlobeMapControl to True, and start the application again.

The window area should now include 3D map information - and you should be able to navigate the 3D-map!


Globe client with 3D map

Globe interaction

Map interaction

Map utilities

The different map utilities can be turned on/off programatically e.g. through User controlls.

Here is an example of adding check boxes with direct binding to the MariaGlobeMapControl from your main window.

...
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Orientation="Horizontal" 
                Visibility="Visible">
        <GroupBox Header="Map Utilities">
            <StackPanel Orientation="Vertical" Visibility="Visible" Margin="5">
                <CheckBox Content="Ruler" Margin="3"
                          ToolTip="Ruler" 
                          IsChecked="{Binding IsRulerVisible, ElementName=MariaGlobeCtrl}" />
                <CheckBox Content="Pan Navigation" Margin="3"
                          ToolTip="Pan Navigation" 
                          IsChecked="{Binding IsPanNavigationVisible, ElementName=MariaGlobeCtrl}" />
                <CheckBox Content="Scale Bar" Margin="3"
                          ToolTip="Center Position, Scale Bar" 
                          IsChecked="{Binding IsScaleBarVisible, ElementName=MariaGlobeCtrl}" />
                <CheckBox Content="Center Mark"  Margin="3"
                          ToolTip="Center Position Indicator"
                          IsChecked="{Binding IsCenterPositionIndicatorEnabled, ElementName=MariaGlobeCtrl}" />
                <CheckBox Content="Mini Map" Margin="3"
                          ToolTip="Mini Map" 
                          IsChecked="{Binding IsMiniMapVisible, ElementName=MariaGlobeCtrl}" />                   
            </StackPanel>
        </GroupBox>
    </StackPanel>
    <Grid Grid.Row="1">
        <mariaglobemapcontrol:MariaGlobeMapControl x:Name="MariaGlobeCtrl" 
                                                   Background="#E9ECFA"                                                   
                                                   Layers="{Binding Layers}"
                                                   MiniMapLayer="{Binding MapViewModel.MiniMapLayer, Mode=OneWay}"
                                                   CenterScale="{Binding MapViewModel.CenterScale}" 
                                                   CenterPosition="{Binding MapViewModel.CenterPosition}"
                                                   Is3DMode="False"
                                                   MouseMoveDistanceToStartTracking="0"
                                                   ZoomOnDblClick="False"
                                                   DegreeToLockRotateAndScale="2"
                                                   PercentageToLockScaleOnly="2.0"
                                                   IsRulerVisible="True" 
                                                   IsCenterPositionIndicatorEnabled="True"
                                                   IsMiniMapVisible="True" />
    </Grid>
</Grid>
...

Map settings

Your map service will most likely contain different map templates, and you would like to select the template to be used.
In we will create a list of templates to chose from, display the current center position and scale, and also choose the display mode - 2D or 3D.

Add the following to your Main Window XAML:

...
<GroupBox Header="Map">
    <Grid Margin="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />                       
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ComboBox Name="cmbActiveMap" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,0,0,5"
                  Width="Auto" Height="Auto" MinWidth="200" 
                  VerticalAlignment="Top"
                  ItemsSource="{Binding MapViewModel.AvailableMapTemplateNames}" 
                  SelectedItem="{Binding MapViewModel.ActiveMapTemplateName}" 
                  IsSynchronizedWithCurrentItem="true" />

        <Label Grid.Row="1" Grid.Column="0"
               Content="Mode" FontWeight="DemiBold"/>
        <StackPanel Grid.Row="1" Grid.Column="1">
            <RadioButton Name="Btn2D" 
                         Content="2D" 
                         IsChecked="True" 
                         Command="{Binding Set2DModeCommand, ElementName=MariaGlobeCtrl}" />
            <RadioButton Name="Btn3D" 
                         Content="3D"                                      
                         Command="{Binding Set3DModeCommand, ElementName=MariaGlobeCtrl}" />
        </StackPanel>

        <Label Grid.Row="2" Grid.Column="0"  FontWeight="DemiBold">
            <TextBlock>
                Center<LineBreak/>
                Scale
            </TextBlock>
        </Label>
        <TextBox Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" 
                 Text="{Binding MapViewModel.CenterScale, StringFormat=N1}" 
                 IsReadOnly="True" />

        <Label Grid.Row="3" Grid.Column="0"  FontWeight="DemiBold">
            <TextBlock>
                Center<LineBreak/>
                Position
            </TextBlock>
        </Label>
        <TextBox Grid.Row="3" Grid.Column="1" VerticalAlignment="Center"
                 Text="{Binding MapViewModel.CenterPosition, Mode=OneWay}" 
                 IsReadOnly="True" />

    </Grid>
</GroupBox>
...

Extend the MapViewModel with:

  • fields and properties to handle display of available map templates
  • initialized event handlers with initialisation and selection of templates
  • CenterPosition and CenterScale properties with notification handling.
...
private Dictionary<string, MapTemplate> _availableMapTemplateDictionary = new Dictionary<string, MapTemplate>();

private double _scale;
private GeoPos _pos;
...
public GeoPos CenterPosition
{
    get { return _pos; }
    set
    {
        _pos = value;
        NotifyPropertyChanged(() => CenterPosition);
    }
}

public double CenterScale
{
    get { return _scale; }
    set
    {
        _scale = value;
        NotifyPropertyChanged(() => CenterScale);
    }
}

public MapViewModel(IMariaMapLayer mapLayer, IMariaMapLayer miniMapLayer)
{
    _mapLayer = mapLayer;

    if (_mapLayer != null)
        _mapLayer.LayerInitialized += OnMapLayerInitialized;

    MiniMapLayer = miniMapLayer;
    if (MiniMapLayer != null)
        MiniMapLayer.LayerInitialized += OnMiniMapLayerInitialized;
}

private void OnMapLayerInitialized()
{
    foreach (var template in _mapLayer.ActiveMapTemplates)
    {
        _availableMapTemplateDictionary.Add(template.Name, template);
    }

    NotifyPropertyChanged(() => AvailableMapTemplateNames);

    CenterPosition = new GeoPos(59.908358, 10.628190); // TPG - Lysaker
    CenterScale = 3500000;

    _mapLayer.ActiveMapTemplate = _mapLayer.ActiveMapTemplates.Any() ?
                                    _mapLayer.ActiveMapTemplates.First() : null;

    _mapLayer.ActiveMapTemplate = PreferredMapTemplate();
    NotifyPropertyChanged(() => ActiveMapTemplateName);
}

private void OnMiniMapLayerInitialized()
{
    MiniMapLayer.ActiveMapTemplate = _mapLayer.ActiveMapTemplates.Any() ?
                                    _mapLayer.ActiveMapTemplates.First() : null;
    MiniMapLayer.ActiveMapTemplate = PreferredMapTemplate();
}



public IEnumerable<string> AvailableMapTemplateNames
{
    get
    {
        return _availableMapTemplateDictionary.Keys.ToList();
    }
}

public string ActiveMapTemplateName
{
    get
    {
        if (_mapLayer != null && _mapLayer.ActiveMapTemplate != null)
            return _mapLayer.ActiveMapTemplate.Name;
        return "";
    }
    set
    {
        MapTemplate template = null;

        if (_availableMapTemplateDictionary.ContainsKey(value))
        {
            template = _availableMapTemplateDictionary[value];
        }
        else if (_availableMapTemplateDictionary.Any())
        {
            template = _availableMapTemplateDictionary.Values.First();
        }

        _mapLayer.ActiveMapTemplate = template;
        MiniMapLayer.ActiveMapTemplate = template;
        NotifyPropertyChanged(() => ActiveMapTemplateName);
    }
}

private MapTemplate PreferredMapTemplate()
{
    var preferred = "TOPO";

    if (_availableMapTemplateDictionary.ContainsKey(preferred))
        return _availableMapTemplateDictionary[preferred];

    return _availableMapTemplateDictionary.Values.Any() ? _availableMapTemplateDictionary.Values.First() : null;
}
...

Running with interaction

Running your application, observe the following:

  • the map utilities are shown/hidden according to the corresponding check boxes
  • the center position and scale are updated when panning/zooming the map
  • the map template drop-down list contains available map templates
  • the map display changes according to selected map template
  • the map display changes according to deleted mode


Preferred map, 2D
Preferred map, 2D

Preferred map, 3D
Preferred map, 3D

Another map, 2D
Another map, 2D

Another map, 3D
Another map, 3D

Draw object interaction

In this section, we will add and update different draw objects, and see how they are presented in the 2D and 3D modes.

Adding draw object layer and draw object view model

To be able to display draw object, we need to include a draw object layer in the globe client.

First, add a view model class, DrawObjectViewModel, to handle the draw object interaction:

  • Inheriting ViewModelBase
  • Add a constructor taking a draw layer object as input
  • Create and implement event handlers for:
    • LayerInitialized
    • ServiceConnected
public class DrawObjectViewModel : ViewModelBase
{
    private IMariaDrawObjectLayer _drawObjectLayer;
    private IMariaService _drawObjectService;

    public DrawObjectViewModel(DrawObjectLayer drawObjectLayer)
    {
        _drawObjectLayer = drawObjectLayer;
        _drawObjectLayer.LayerInitialized += DrawObjectLayer_LayerInitialized;
        _drawObjectLayer.ServiceConnected += DrawObjectLayer_ServiceConnected;
    }

    private void DrawObjectLayer_LayerInitialized()
    {
        _drawObjectService = new MariaService(
            "DrawObjectService",
            "http://localhost:9008/drawobjects",
            BindingType.BasicHttp);

        _drawObjectLayer.DrawObjectServices = new ObservableCollection<IMariaService> { _drawObjectService };

        _drawObjectLayer.CreateDrawObjectList("local", true);
    }

    private void DrawObjectLayer_ServiceConnected(object sender, MariaServiceEventArgs args)
    {
        _drawObjectLayer.ActiveDrawObjectService = _drawObjectLayer.DrawObjectServices[0];

        _drawObjectLayer.CreateDrawObjectList("test", false);
        _drawObjectLayer.ActiveDrawObjectList = "test";
    }
}

In the MainViewModel, add a draw object layer field, and in the constructor, add creation of the layer and DrawObjectViewModel and add the layer to the Layers list.

...
public DrawObjectViewModel DrawObjectViewModel { get; private set; }

public MainViewModel()
{
    ...
    DrawObjectLayerFactory = new DrawObjectLayerFactory();
    _drawObjectLayer = new DrawObjectLayer(GlobeMapManager, DrawObjectLayerFactory, true)
        { InitializeCreationWorkflows = true };
    DrawObjectViewModel = new DrawObjectViewModel(_drawObjectLayer);
    Layers.Add(_drawObjectLayer);
}
...

Draw object utilities

Add the following GUI elements to your main window:

  • One button for adding/updating a volume object.
  • One button for adding/updating a line object.
  • One button for adding/updating a point object.

The XAML could look something like this:

...
<GroupBox Header="Draw Object" >
    <StackPanel VerticalAlignment="Top">
        <Button Margin="3"                         
                Content="Volume" 
                Command="{Binding VolumeTestCmd}"/>
        <Button Margin="3"                         
                Content="Line" 
                Command="{Binding LineTestCmd}"/>
        <Button Margin="3"                         
                Content="Point" 
                Command="{Binding PointTestCmd}"/>     
    </StackPanel>
</GroupBox>
...

Implement command handlers for the draw object utilities in the MainViewModel:

...
public ICommand VolumeTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateVolumeObject); } }
public ICommand LineTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdateLineObject); } }
public ICommand PointTestCmd { get { return new DelegateCommand(DrawObjectViewModel.CreateOrUpdatePointObject); } }
...

Implement the command handler delegates in the DrawObjectViewModel.

...
public void CreateOrUpdatePointObject(object obj)
{
    var pos = RandomProvider.GetRandomPosition(_drawObjectLayer.GeoContext.Viewport.GeoRect);

    var altitude = RandomProvider.GetRandomInt(1, 5) * 500.0;

    var pointObject = _drawObjectLayer.DrawObjectFactory.CreateTacticalGraphic("STBOPS.OPN.HJKG.APL");
    pointObject.TacticalDataFields.StandardIdentity = StandardIdentity.Hostile;
    pointObject.TacticalDataFields.Name = "Globe-Point";
    pointObject.Id = new ItemId("local", "Globe-Point");

    pointObject.Points[0] = new GeoPoint() { Latitude = pos.Lat, Longitude = pos.Lon, Altitude = altitude };
    _drawObjectLayer.UpdateStore(pointObject);
}

public void CreateOrUpdateVolumeObject(object obj)
{

    var volumeObject = _drawObjectLayer.DrawObjectFactory.CreateTacticalGraphic("TACGRP.FSUPP.ARS.C2ARS.ACA.IRR");
    volumeObject.TacticalDataFields.StandardIdentity = StandardIdentity.Friendly;
    volumeObject.Id = new ItemId("local", "Globe-Volume");

    var pos = RandomProvider.GetRandomPosition(_drawObjectLayer.GeoContext.Viewport.GeoRect, 0.8);
    var factorLat = _drawObjectLayer.GeoContext.Viewport.GeoRect.DeltaLat * RandomProvider.GetRandomDouble(0.05, 0.1);
    var factorLon = _drawObjectLayer.GeoContext.Viewport.GeoRect.DeltaLon * RandomProvider.GetRandomDouble(0.05, 0.1);

    volumeObject.Points = new GeoPoint[] {
        new GeoPoint() { Latitude = pos.Lat + factorLat, Longitude = pos.Lon + factorLon },
        new GeoPoint() { Latitude = pos.Lat + factorLat, Longitude = pos.Lon - factorLon },
        new GeoPoint() { Latitude = pos.Lat - factorLat, Longitude = pos.Lon - factorLon },
        new GeoPoint() { Latitude = pos.Lat - factorLat, Longitude = pos.Lon + factorLon },
    };

    _drawObjectLayer.UpdateStore(volumeObject);
}

public void CreateOrUpdateLineObject(object obj)
{
    var lineObject = _drawObjectLayer.DrawObjectFactory.CreatePolyLine(new ItemId("local", "Globe-Line"));
    var pt0 = RandomProvider.GetRandomPosition(_drawObjectLayer.GeoContext.Viewport.GeoRect, 0.5);
    var pt1 = RandomProvider.GetRandomPosition(_drawObjectLayer.GeoContext.Viewport.GeoRect, 0.5);
    var pt2 = RandomProvider.GetRandomPosition(_drawObjectLayer.GeoContext.Viewport.GeoRect, 0.5);

    lineObject.Points = new GeoPoint[] {
        new GeoPoint() { Latitude = pt0.Lat, Longitude = pt0.Lon, Altitude = RandomProvider.GetRandomInt(1, 5) * 500.0 },
        new GeoPoint() { Latitude = pt1.Lat, Longitude = pt1.Lon, Altitude = RandomProvider.GetRandomInt(1, 5) * 500.0 },
        new GeoPoint() { Latitude = pt2.Lat, Longitude = pt2.Lon, Altitude = RandomProvider.GetRandomInt(1, 5) * 500.0 },
    };

    lineObject.GenericDataFields.LineColor = Colors.Navy;
    var dashStyle = DashStyles.DashDot;
    lineObject.GenericDataFields.LineDashStyle = dashStyle.Dashes.ToList();
    lineObject.GenericDataFields.LineDashStyleOffset = dashStyle.Offset;
    lineObject.GenericDataFields.LineWidth = 6.0;

    _drawObjectLayer.UpdateStore(lineObject);
}
...

The RandomProvider class is described in context with Map interaction client, track creation.

Track interaction

Autofollow

Monitoring service connections