PictureMarkerSymbol moves with scene and altitude is 0

3106
4
08-13-2015 10:38 AM
JasonTrinidad1
New Contributor III

My code generates a points that has a PictureMarkerSymbol. It is run over and over, and the previously generated graphic is removed and a new one is added (so it looks like the plane is moving, does not have to be a fluent animation).  My issues is that I am able to add the initial heading of the symbol, so it has the correct heading, but the heading never changes afterwards. Also, when I rotate the map, the icon's heading seems to be fixed with the screen (always points to the same place with respect of the screen. Also, the altitude is always touching the ground.

esri1.JPG

esri2.JPG

(same point looked from another angle)

How do I make the direction of the icon always point to the real heading, and how can I have some elevation?

My code:

var graphicsOverlay = MySceneView.GraphicsOverlays["MyGraphicsOverlay"] as Esri.ArcGISRuntime.Controls.GraphicsOverlay;

if (graphicsOverlay == null)

{

    graphicsOverlay = new GraphicsOverlay();

    graphicsOverlay.ID = "MyGraphicsOverlay";

}

//Convert from latlon to ArcGis Window coordinate

var scenePoint =  new Esri.ArcGISRuntime.Geometry.MapPoint(lon,lat,alt,MySceneView.SpatialReference);

var pointSceneGraphic = new Esri.ArcGISRuntime.Layers.Graphic();

pointSceneGraphic.Geometry = scenePoint;

//set the graphic of the placemarker

Esri.ArcGISRuntime.Symbology.PictureMarkerSymbol imgSym = new Esri.ArcGISRuntime.Symbology.PictureMarkerSymbol();

imgSym.SetSourceAsync(new Uri(iconHref));

imgSym.AngleAlignment = Esri.ArcGISRuntime.Symbology.MarkerAngleAlignment.Map;

imgSym.Angle = platformHeading;

                   

pointSceneGraphic.Symbol = imgSym;

ret = pointSceneGraphic.GetHashCode().ToString();

MySceneView.GraphicsOverlays[0].Graphics.Add(pointSceneGraphic);

foreach (var graphicsObj in graphicsOverlay.Graphics)

{

    if (graphicsObj.GetHashCode().ToString() == oldId)

    {

        graphicsOverlay.Graphics.Remove(graphicsObj);

        break;

    }

}

My WPF looks like this

<Grid>

        <esri:SceneView x:Name="MySceneView" Visibility="Visible" LayerLoaded="MySceneView_LayerLoaded">

            <esri:Scene>

                <esri:ArcGISTiledMapServiceLayer ID="BaseScene" ResamplingMode="None"

                    ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer" />

            </esri:Scene>

        </esri:SceneView>

</Grid>

Is there a better way to do what I'm doing?

0 Kudos
4 Replies
AnttiKajanus1
Regular Contributor II

Here is example how to implement symbol rotation / height in Store app (just follow the same approach if you are working with wpf).

<Page

  x:Class="ArcGISRuntime.Samples.Store.Samples.SymbolRotation3d"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  xmlns:local="using:ArcGISRuntime.Samples.Store.Samples"

  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

  xmlns:esri="using:Esri.ArcGISRuntime.Controls"

  xmlns:layers="using:Esri.ArcGISRuntime.Layers"

  xmlns:symb="using:Esri.ArcGISRuntime.Symbology"

  xmlns:sceneSymb="using:Esri.ArcGISRuntime.Symbology.SceneSymbology"

  mc:Ignorable="d">

  <Grid>

  <Grid.Resources>

  <symb:SimpleRenderer x:Key="SimpleDiamondRenderer">

  <symb:SimpleRenderer.SceneProperties>

  <symb:RendererSceneProperties HeadingExpression="[Heading]"

   PitchExpression="[Pitch]"

   RollExpression="[Roll]" />

  </symb:SimpleRenderer.SceneProperties>

  <symb:SimpleRenderer.Symbol>

  <sceneSymb:DiamondMarkerSymbol Color="Blue" Width="1000" Height="2000"/>

  </symb:SimpleRenderer.Symbol>

  </symb:SimpleRenderer>

  <symb:SimpleRenderer x:Key="SimpleBoxRenderer">

  <symb:SimpleRenderer.SceneProperties>

  <symb:RendererSceneProperties HeadingExpression="[Heading]"

   PitchExpression="[Pitch]"

   RollExpression="[Roll]" />

  </symb:SimpleRenderer.SceneProperties>

  <symb:SimpleRenderer.Symbol>

  <sceneSymb:BoxMarkerSymbol Width="1000" Height="1000" Depth="1000" Color="Red"/>

  </symb:SimpleRenderer.Symbol>

  </symb:SimpleRenderer>

  </Grid.Resources>

  <esri:SceneView x:Name="MySceneView" LayerLoaded="MySceneView_LayerLoaded">

  <esri:Scene>

  <esri:Scene.Surface>

  <esri:ServiceElevationSource ServiceUri="http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"

  IsEnabled="True" />

  </esri:Scene.Surface>

  <layers:ArcGISTiledMapServiceLayer ID="AGOLayer"

  ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer" />

  <!--<layers:GraphicsLayer ID="DiamondGraphicsLayer" Renderer="{StaticResource SimpleDiamondRenderer}">

  <layers:GraphicsLayer.SceneProperties>

  <layers:LayerSceneProperties SurfacePlacement="Draped"/>

  </layers:GraphicsLayer.SceneProperties>

  </layers:GraphicsLayer>

  <layers:GraphicsLayer ID="BoxGraphicsLayer" Renderer="{StaticResource SimpleBoxRenderer}">

  <layers:GraphicsLayer.SceneProperties>

  <layers:LayerSceneProperties SurfacePlacement="Draped"/>

  </layers:GraphicsLayer.SceneProperties>

  </layers:GraphicsLayer>-->

  </esri:Scene>

  <esri:SceneView.GraphicsOverlays>

  <esri:GraphicsOverlay ID="DiamondGraphicsLayer" Renderer="{StaticResource SimpleDiamondRenderer}">

  <esri:GraphicsOverlay.SceneProperties>

  <layers:LayerSceneProperties SurfacePlacement="Relative"/>

  </esri:GraphicsOverlay.SceneProperties>

  </esri:GraphicsOverlay>

  <esri:GraphicsOverlay ID="BoxGraphicsLayer" Renderer="{StaticResource SimpleBoxRenderer}">

  <esri:GraphicsOverlay.SceneProperties>

  <layers:LayerSceneProperties SurfacePlacement="Draped"/>

  </esri:GraphicsOverlay.SceneProperties>

  </esri:GraphicsOverlay>

  </esri:SceneView.GraphicsOverlays>

  </esri:SceneView>

  <Grid HorizontalAlignment="Right" VerticalAlignment="Top" Margin="10" Width="100">

  <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="4" Background="LightGray">

  <StackPanel>

  <StackPanel Background="Transparent" Margin="5">

  <TextBlock x:Name="txtHeading" Text="Heading" Foreground="Black"/>

  <Slider x:Name="HeadingSlider" Minimum="0" Maximum="360" ValueChanged="OnHeadingSliderChanged"/>

  </StackPanel>

  <StackPanel Background="Transparent" Margin="5">

  <TextBlock x:Name="txtPitch" Text="Pitch"  Foreground="Black"/>

  <Slider x:Name="PitchSlider" Minimum="0" Maximum="360" ValueChanged="OnPitchSliderChanged"/>

  </StackPanel>

  <StackPanel Background="Transparent" Margin="5">

  <TextBlock x:Name="txtRoll" Text="Roll"  Foreground="Black"/>

  <Slider x:Name="RollSlider" Minimum="0" Maximum="360" ValueChanged="OnRollSliderChanged"/>

  </StackPanel>

  </StackPanel>

  </Border>

  </Grid>

  </Grid>

</Page>

using Esri.ArcGISRuntime.Controls;

using Esri.ArcGISRuntime.Geometry;

using Esri.ArcGISRuntime.Layers;

using Esri.ArcGISRuntime.Symbology;

using Esri.ArcGISRuntime.Symbology.SceneSymbology;

using System;

using System.Linq;

using Windows.UI.Xaml.Controls;

using Windows.UI.Xaml.Controls.Primitives;

namespace ArcGISRuntime.Samples.Store.Samples

{

  public sealed partial class SymbolRotation3d : Page

  {

  public SymbolRotation3d()

  {

  this.InitializeComponent();

  }

  private void MySceneView_LayerLoaded(object sender, Esri.ArcGISRuntime.Controls.LayerLoadedEventArgs e)

  {

  if (e.LoadError == null && e.Layer.ID == "AGOLayer")

  {

  MySceneView.SetViewAsync(new Camera(new MapPoint(-106.57, 39.01, 14614.24), 281.66, 74.47), new TimeSpan(0, 0, 3), true);

  AddGraphics();

  }

  }

  private void AddGraphics()

  {

  Graphic graphic = new Graphic(new MapPoint(-106.981, 39.028, 6000, SpatialReferences.Wgs84));

  // Add a graphic to each graphics layer. It will use the renderer specified in the XAML to render the graphic.

  foreach (GraphicsOverlay gOverlay in MySceneView.GraphicsOverlays)

  {

  gOverlay.Graphics.Add(graphic);

  }

  }

  /// <summary>

  /// Change the Heading of the graphics based on the slider values (0-360).

  /// </summary>

  private void OnHeadingSliderChanged(object sender, RangeBaseValueChangedEventArgs e)

  {

  foreach (GraphicsOverlay gOverlay in MySceneView.GraphicsOverlays)

  {

  foreach (Graphic g in gOverlay.Graphics)

  {

  g.Attributes["Heading"] = (sender as Slider).Value;

  }

  }

  // Display the slider Heading value

  txtHeading.Text = String.Format("Heading: {0:0.00}", (sender as Slider).Value.ToString());

  }

  /// <summary>

  /// Change the Pitch of the graphics based on the slider values (0-360)

  /// </summary>

  private void OnPitchSliderChanged(object sender, RangeBaseValueChangedEventArgs e)

  {

  foreach (GraphicsOverlay gOverlay in MySceneView.GraphicsOverlays)

  {

  foreach (Graphic g in gOverlay.Graphics)

  {

  g.Attributes["Pitch"] = (sender as Slider).Value;

  }

  }

  // Display the slider Pitch value

  txtPitch.Text = String.Format("Pitch: {0:0.00}", (sender as Slider).Value.ToString());

  }

  /// <summary>

  /// Change the Roll of the graphics based on the slider values (0-360)

  /// </summary>

  private void OnRollSliderChanged(object sender, RangeBaseValueChangedEventArgs e)

  {

  foreach (GraphicsOverlay gOverlay in MySceneView.GraphicsOverlays)

  {

  foreach (Graphic g in gOverlay.Graphics)

  {

  g.Attributes["Roll"] = (sender as Slider).Value;

  }

  }

  // Display the slider Roll value

  txtRoll.Text = String.Format("Roll: {0:0.00}", (sender as Slider).Value.ToString());

  }

  }

}

JasonTrinidad1
New Contributor III

Thanks. I'll try it out.

0 Kudos
AnttiKajanus1
Regular Contributor II

Also if you are using a plane picture in 3d, i would recommend to use ModelMarkerSymbol with 3d plane in instead of simple picture.

0 Kudos
JasonTrinidad1
New Contributor III

I managed to get the elevation working, but the heading is still off. When I create the first graphics object, the heading is correct. This function is ran over and over, deleting the previous graphic, and adding a new one. All the new placemarks have the angle of the first placemark ever created. I also added MySceneView_CameraChanged to get the current rotation of the scene in order to correct the angle of the palcemark, but still nothing.

var graphicsOverlay = MySceneView.GraphicsOverlays["MyPlacemarkLayer"] as Esri.ArcGISRuntime.Controls.GraphicsOverlay;

            

Graphic graphic = new Graphic(new MapPoint(lon, lat, alt,MySceneView.SpatialReference));

Esri.ArcGISRuntime.Symbology.PictureMarkerSymbol imgSym = new Esri.ArcGISRuntime.Symbology.PictureMarkerSymbol();

imgSym.SetSourceAsync(new Uri(iconHref));

imgSym.AngleAlignment = Esri.ArcGISRuntime.Symbology.MarkerAngleAlignment.Map; //Does this even do anything

imgSym.Angle = platformHeading + _mapRotation;

            

ret = graphic.GetHashCode().ToString();

graphic.Symbol = imgSym;

//graphic.Attributes["Heading"] = _mapRotation + _mapRotation; //This doesn't do anything

MySceneView.GraphicsOverlays["MyPlacemarkLayer"].Graphics.Add(graphic);

for (int i = 0; i < MySceneView.GraphicsOverlays["MyPlacemarkLayer"].Graphics.Count; i++)

{

    if (MySceneView.GraphicsOverlays["MyPlacemarkLayer"].Graphics.GetHashCode().ToString() == oldId)

    {

        MySceneView.GraphicsOverlays["MyPlacemarkLayer"].Graphics.RemoveAt(i);

        break;

    }

                

}

private void MySceneView_CameraChanged(object sender, EventArgs e)

{

        _mapRotation = MySceneView.GetCurrentViewpoint(ViewpointType.CenterAndScale).Rotation;

        canvasBox.RenderTransform = new RotateTransform(360 - _mapRotation, canvasBox.Width / 2, canvasBox.Height / 2);

        foreach (GraphicsOverlay gOverlay in MySceneView.GraphicsOverlays)

        {

            foreach (Graphic g in gOverlay.Graphics)

            {

                g.Attributes["Heading"] = (double)g.Attributes["Heading"] + _mapRotation;

            }

        }

}

In this example, I set my heading to be 45 and to increment by 5 every iteration. The placemark seems to get stuck at 45 forever.

WUit7Q.gif

This is my WPF

<Grid>

<Grid.Resources>

  <esri:SimpleRenderer x:Key="MyRenderer">

   <esri:SimpleRenderer.SceneProperties >

    <esri:RendererSceneProperties

      HeadingExpression="[Heading]"

      PitchExpression="[Pitch]"

      RollExpression="[Roll]" />

   </esri:SimpleRenderer.SceneProperties>   

   <esri:SimpleRenderer.Symbol>

    <esri:PictureMarkerSymbol AngleAlignment="Map" />

   </esri:SimpleRenderer.Symbol>

  </esri:SimpleRenderer>

</Grid.Resources>

<esri:SceneView x:Name="MySceneView" Visibility="Visible"  LayerLoaded="MySceneView_LayerLoaded" CameraChanged="MySceneView_CameraChanged" >

  <esri:Scene>

   <esri:Scene.Surface>

    <esri:ServiceElevationSource ServiceUri="http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"  IsEnabled="True" />

   </esri:Scene.Surface>

   <esri:ArcGISTiledMapServiceLayer ID="BaseScene"

     ServiceUri="http://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer" />

  </esri:Scene>

  <esri:SceneView.GraphicsOverlays>

   <esri:GraphicsOverlay ID="MyPlacemarkLayer" Renderer="{StaticResource MyRenderer}" RenderingMode="Dynamic">

    <esri:GraphicsOverlay.SceneProperties>

     <esri:LayerSceneProperties SurfacePlacement="Relative"/>

    </esri:GraphicsOverlay.SceneProperties>

   </esri:GraphicsOverlay>

  </esri:SceneView.GraphicsOverlays>

</esri:SceneView>

</Grid>

0 Kudos