Sdílet prostřednictvím


Geometry

The first thing that most people want to do when playing with the 3D control is add some stuff to the world. The most straightforward way to do this is via the Geometry Manager, accessible from Host.Geometry. You can modify any of the samples to contain this code, most easily in the Activate function of a plug-in or OnLoad for SimpleForm.

      Host.Geometry.AddGeometry(new PushpinGeometry(

      "My Layer", "My Id",

      LatLonAlt.CreateUsingDegrees(-47.6435, 112.1422, 100.0),

      PushpinInfo.Default));

This creates a default pin on top of Microsoft Building 116 in Redmond, WA, just in case you were wondering where that is. The first two parameters are just identifiers. You can use Layers to group items together, but it's not required. The two strings together just have to be unique. The third parameter is the position of the pin in Latitude, Longitude, Altitude. Here we’re using degrees in WGS84, and meters for the altitude.

Let me digress on units for a moment. Notice that latitude, which is the “Y” component of the coordinate, comes first. This is a long-standing convention when specifying geographic coordinates, but for our purposes is somewhat arbitrary. In other contexts, for example when we use a Coordinate2D struct, which is a more general-use structure not tied to a specific coordinate system, we’ll follow the mathematical convention of “X” first. Also, values of latitude and longitude are technically angles (from the equator and around the polar axis, respectively). When dealing with angles in computers it is convenient to store them as radians, which is how most trigonometric functions deal with them. However, degrees are usually easier to think about. So here we create the structure explicitly in degrees to reduce confusion. If you ever have trouble with your pushpin not appearing, the first thing to check is the order of your values and their units. We use meters for distances and altitudes, but the exact meaning of altitude can change depending on what AltitudeMode you are associating with the geometry.

Back to the pushpin, next we’ll want to change how it appears. If you pull the PushpinInfo.Default object into a local variable, you can then modify the various provided values, leaving others as they are.

      PushpinInfo info = PushpinInfo.Default;

      info.Resource = https://kristoffer.members.winisp.net/VE3D/jewel.png;

      info.OrientationMode = PushpinOrientationMode.UseProvided;

      info.Orientation = new Orientation(new RollPitchYaw(

      0.0,

      20 * Constants.RadiansPerDegree,

      45 * Constants.RadiansPerDegree));

      Host.Geometry.AddGeometry(new PushpinGeometry(

      "My Layer", "My Id",

      LatLonAlt.CreateUsingDegrees(47.6435, -122.1422, 10.0),

      info));

In this case we’ve added a pretty logo over the same building rather than using the default orange pin, and told it to use a specific orientation instead of always facing the camera. Roll/Pitch/Yaw (RPY) is in degrees, but we can convert using some constants. Think of the image as a face, with the nose pointing straight out of the middle of it. If you supply a RPY of (0, 0, 0) the face will point north, toward the horizon, with the head level. Rolling will be like tilting the head sideways towards the shoulders, pitch is like looking up and down, and yaw is looking side to side. In this case, we are looking slightly up and to the northwest. You can combine the values with values from the camera to get interesting effects by setting OrientationMode. AltitudeMode is set here, to control the exact meaning of the altitude component of its position.

The two other basic types of geometry are PolylineGeometry and PolygonGeometry. Their setup is similar, except that you will provide arrays of LatLonAlt values. There are also two distinct categories of each, those that are drawn on the ground and those drawn in the air.

Host.Geometry.AddGeometry(new PolygonGeometry(

      "My Layer", "My Poly",

      null,

      new LatLonAlt[] {

      LatLonAlt.CreateUsingDegrees(47.6435, -122.1422, 0.0),

      LatLonAlt.CreateUsingDegrees(47.645, -122.1422, 0.0),

      LatLonAlt.CreateUsingDegrees(47.645, -122.1412, 0.0),

      LatLonAlt.CreateUsingDegrees(47.6435, -122.1412, 0.0) },

      PolygonGeometry.PolygonFormat.Polygon2D,

      PolyInfo.DefaultPolyline));

The Format parameter specifies what type they should be, and altitude values will be ignored for 2D versions (note: LineList2D is currently not supported, but the rest all have their uses). The third parameter here is a “tag”, which is something that can be used when your user is interacting with your objects via the mouse. In the Geometry sample you can see some of these ideas (and a few other things) in play.

Comments

  • Anonymous
    December 25, 2008
    Hi there, I’m working on porting my Location Aware WPF Application to Virtual Earth 3D Managed WinForms

  • Anonymous
    March 02, 2009
    Why does the code not work when you change PolygonGeometry to PolylineGeometry? It doesn't seem to render anything.

  • Anonymous
    March 02, 2009
    It is supposed to work.  Could you post the code you are using?

  • Anonymous
    March 14, 2009
    Hey there!  I'm wondering if you might help me understand something a little better.  I've been trying to work with Pushpins on the GlobeControl by trying to change the direction they point in, with little success. I have a 2D image of an airplane, simple and basic, and if you look at it the image in any image viwer, its just the basic outline of an airport with its nose pointing "up". I have a given "course" of this airplane, so I want to have the airplane "point" in the right direction, but I can't seem to pull it off such that the airplane still faces the camera yet orients the airplanes "course" in the right direction.  The icon either gets thin (points toward the ground) or some other funky thing.  I've tried varying combinations of the Pushpin.OrientationMode to no avail. Can you suggest the right combination to get this icon to always face the camera, yet "point" toward the course its headed in? Here's my current code snippet: Aircraft.OrientationMode = PushpinOrientationMode.CameraUpFaceCamera Dim rpw As New RollPitchYaw(_course, 0, 0) Aircraft.Orientation = New Microsoft.MapPoint.Geometry.VectorMath.Orientation(rpw) _pushpinGeometry = New PushpinGeometry(_layer, _ident & "Icon", Me, _                                           LatLonAlt.CreateUsingDegrees(Lat, Lon, _altitude / 3), Aircraft)

  • Anonymous
    March 15, 2009
    It's hard to be certain exactly what you want (these things are often hard to describe in words), but it sounds like you want something that is always facing the screen but is simply rotated in screen space.  Pushpins won't exactly do that.  You may be interested in investigating the SpriteGraphicsObject (see BunnyActor in the ActorDataSource sample), and adjusting the Rotation property each frame. public MyActor( Bitmap image, LatLonAlt position, BindingsSource source) : base(source) { this.renderable = new SpriteGraphicsObject(true); renderable.Texture = Texture.FromBitmap(image); renderable.AlphaEnable = true; renderable.Mode = SpriteMode.WorldCoordinates; this.renderable.Position = position.GetVector(); } public override void Update(SceneState sceneState) { CameraData c; if (sceneState.TryGetData<CameraData>(out c)) { double heading = 45; GeodeticViewpoint v = new GeodeticViewpoint(); v.Position.Vector = this.renderable.Position; // local means relative to the earth v.LocalOrientation.Yaw = Constants.RadiansPerDegree * heading; // Now we want to find a world position that is in the direction we want to face Vector3D newV = this.renderable.Position + (v.Orientation.LookAt * // this is relative to our absolute cartesian system (c.MetersAboveGround * 0.25)); // scale by camera height to avoid numerical instability // project those onto the screen Point planeP = c.GlobalToScreen(this.renderable.Position); Point newP = c.GlobalToScreen(newV); Vector2D planeScreen = new Vector2D(planeP.X, planeP.Y); Vector2D newScreen = new Vector2D(newP.X, newP.Y); Vector2D screenDirection = newScreen - planeScreen; // need to be careful when the points are nearly on top of one another, as when you are looking // straight along the heading.  The numbers will get very sensitive to small movements. if (screenDirection.Length() < 10) { screenDirection.X = 0; screenDirection.Y = -1; } // and finally rotate the sprite by the correct amount double rotate = Vector2D.GetAngle( new Vector2D(0, -1), // towards top of screen (up is negative y) screenDirection); this.renderable.Rotation = -rotate; } } Add your actor to Host.Actors.  You should be able to adjust heading and position dynamically.  You can also adjust renderable.Scale by distance from the camera if you like.

  • Anonymous
    March 20, 2009
    Wow, thanks a ton for the help and code samples.  I'm really shocked that the basic pushpin doesn't provide that kind of functionality though. Using your approach, will I still be able to set a real-world altitude for the aircraft?  I'm essentially hoping to render the aircraft at its real-world altitude, heading, and position... Again, thanks for the in-depth help.

  • Anonymous
    March 22, 2009
    When you set position in the ctor (and in a property if you choose) you can specify in terms of LatLonAlt, which contains altitude information.  This alt will be relative to the WGS84 datum rather than the ground, though.  I believe that aircraft often specify altitude in terms of distance from sea level.  To get that, when you set position on the renderable do code like this: LatLonAlt Position { } I don't disagree with you on the functionality issue, but believe it or not you actually are the first person to ask for it.  I'll make sure that it is included for our future feature list.

  • Anonymous
    March 22, 2009
    oops, fat fingered that one. Position (set) { renderable.Position = new LatLonAlt(value.Lat, value.Lon, value.Alt + value.SeaLevel()); }

  • Anonymous
    April 20, 2009
    Ok, so I managed to get it all working thanks to your code, and as far as the panning and moving around the globe, everything is working great.  FYI, this is a Surface application, so it's pretty cool to play with. HOWEVER... I started trying to get crafty and work some WPF labels above the icons so that the aircraft can be identified.  In the render thread, in the Actor's Render method, I call Dispatcher.Invoke to set change the label's position on a transparent canvas above the Map control.  It works, seemingly... I can stare at the map and not manipulate it, and the label moves with the aircraft just fine. The minute I try to move the map, I can move it around and manipulate the map for about a half a second to a second, then it locks up - and it seems like all of the threads lock up.  The Render method is no longer called, and I'm not able to move the map. Any suggestions as to what might cause this? Here's the Render method of my Actor;    Public Overrides Sub Render(ByVal sceneState As SceneState)        Dim renderQueues As RenderQueues = sceneState.GetData(Of RenderQueues)()        Dim v1 As Vector3D = sceneState.GetData(Of CameraData).Snapshot.Position.Vector        Dim v2 As Vector3D = Me.Renderable.Position        Dim distance As Double = myDistance(v1, v2)        Me.SetScale(distance)        If _selected Then            Dim c As CameraData            If sceneState.TryGetData(Of CameraData)(c) Then                Dim planeP As System.Drawing.Point = c.GlobalToScreen(Me.Renderable.Position)                If Not IsNothing(label) Then                    Dim d As New _labelDelegate(AddressOf SetLabelPosition)                    label.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Background, d, planeP)                End If            End If        End If        renderQueues.AddAlphaRenderable(CDbl(CInt(distance)), Me.Renderable)    End Sub

  • Anonymous
    April 20, 2009
    PS:  Here's the SetLabelPosition sub    Public Sub SetLabelPosition(ByVal p As System.Drawing.Point) ' runs on UI thread        'Debug.Print("x: " & p.X & ", y: " & p.Y)        'Debug.Print("Here" & Now.Ticks)        If p.X < 1025 AndAlso p.Y < 769 Then            Me.Canvas.SetTop(Me.label, p.Y - 32)            Me.Canvas.SetLeft(Me.label, p.X - 30)        End If        'Debug.Print("Exitting")    End Sub

  • Anonymous
    April 20, 2009
    I'm not familiar with Dispatcher, but it seems likely to be the problem.  A few suggestions, probably in order of desirability: If Invoke is synchronous try an async flavor (in winforms it's "BeginInvoke").   We also have a wrapper for similar functionality: WorldEngine.ExecuteOnUIThread, as this sort of thing can get hairy.   You could set the new position in simple fields on the Label instead of invoking, and use WPF's CompositionTarget.Render to fix up the actual canvas properties. Finally, we also have our own 2D text rendering code, ScreenTextActor/TextGraphicsObject.  However there's no reason you can't mix WPF and 3D elements.

  • Anonymous
    April 20, 2009
    BeginInvoke did the trick :)  Thanks again for all your help.  I'll post a link to a video demo of the app when it's ready :)

  • Anonymous
    July 09, 2009
    The comment has been removed

  • Anonymous
    July 09, 2009
    Oops, nevermind.  I figured out that the trick is to make sure the polyline is PolylineFormat.Polyline2D.

  • Anonymous
    July 09, 2009
    That is indeed the trick. We don't support anti-aliasing.  Sorry.  You can often force it on with driver utilities, but that's not the answer you want :)

  • Anonymous
    August 18, 2009
    How do I do hit testing with PolygonGeometry?  I see that PolyInfo has a hitdetect property. I assume I set that to On and then look check for intersects?  

  • Anonymous
    February 09, 2010
    I am using an image for the pushpin image.  Is there a way I can make the pushpingeometry center over the location, rather than placing the image above the location since it still assumes the pushpin arrow is there?

  • Anonymous
    February 09, 2010
    Jun: info.ImageOffset = new Coordinate2D(0, X); Where X is half the height of your image, in pixels.  That does mean you need to know the size of the image upfront.