WPF 3D: Google maps on a sphere, or ‘The World’

April 6, 2008

In preparation of porting my Offline Maps application to WPF, I started experimenting with WPF 3D. In perticular, I wanted to apply the level 2 zooming of Google maps to a sphere as a texture mapping. This post describes this simple objective.

Volumes in WPF

I will not spend to much time describing the basics of representing surfaces and volumes in WPF 3D. There are a lot of beginner tutorials on the internet describing the meaning of meshes, triangularisation, etc… The article that got me going is Windows Presentation Foundation (WPF) 3D Tutorial

There is also the 3D Tools for the Windows Presentation Foundation library to help you with constructing basic 3D volumes. However, I did not use it. I wanted to construct the sphere myself to get a grasp at what was needed to do this. I did use the Generating a sphere-mesh in XAML article on Codeproject to get me going but to understand what they are doing I reconstructed the code from scratch.

Texture mapping in WPF

Again, there is enough material about this on the internet explaining the basics. The article that got me going is the Texture Coordinates and Visuals on 3D surfaces article.

How to create a sphere

Let me start by saying that we will not create a sphere. We will create “patches” of a sphere. Following figure vshows what I mean by a “patch”:

Why we are creating patches and not a full sphere will be explained when talk about texture mapping. The basic reason is that you can not apply a bitmap texture to only a part of a geometry (at least, not that I know of) because the mapping is done by providing a collection of coordinates in the bitmap which map to the 3D-point collection off the geometry. As the mapping occurs by the position of the points in these collections, both collections have to be equal in size.

The Generating a sphere-mesh in XAML article mentions two ways to generate a spherical surface, but the one used in that article fits our goal very nicely: because Google tiles are basicaly squares cut from a mercator mapping of the world with horizontal lines being of equal latitude and vertical lines being of equal longitude, that fits perfect with the UVsphere way of sphere generation, in contrast with the Ikosaeder way of sphere generation.

Our patches do the same: they are limited by a starting and ending horizontal angle, corresponding with the limiting longitude and a starting and ending vertical angle, corresponding with the limiting latitude of the tile.

To get the cartesian coordinates of the points on the spherical patch, we transfrom the spherical coordinates provided by the longitude-latitude couple to xyz-coordinates. Wikipedia’s ‘Spherical coordinate system’ article provides some explanation on the process.

In code this gives:

// vertical variation
for  (int zstep = 0; zstep <= zmax; zstep++)
{
    // the z-coordinate is the projection of the 
    //  sphere's radius along the z-axis
    double zcoord = m_radius * 
        Math.Sin(m_startVerticalAngle  
            + verticalAngleStep * zstep);
    // the projection on the xy-plane is the radius  
    //  of the arc in the  xy-plane
    double xyproj = m_radius *  
        Math.Cos(m_startVerticalAngle 
             + verticalAngleStep * zstep);
 
    // horizontal variation
     for (int rstep = 0; rstep <= rmax; rstep++)
    {
        // The resulting x and y coordinate are the 
         //  projection of the arc's radius on the 
        //  respective axis.
        double xcoord = xyproj *  
            Math.Cos(m_startHorizontalAngle 
                 + horizontalAngleStep * rstep);
         double ycoord = xyproj * 
             Math.Sin(m_startHorizontalAngle 
                + horizontalAngleStep * rstep);
 
        m_positions.Add(
            new Point3D(xcoord, ycoord, zcoord));
         m_normals.Add(
            new Vector3D(xcoord, ycoord, zcoord));
    }
}

The triangularisation is done as follows:

which in code gives us:

for (int zstep = 0; zstep < zmax; zstep++)
{
    for (int rstep = 0; rstep < rmax; rstep++)
    {
        m_triangles.Add(rstep 
            + zstep * (rmax + 1));
        m_triangles.Add(rstep 
             + zstep * (rmax + 1) + 1);
         m_triangles.Add(rstep 
            + zstep * (rmax +  1) + (rmax + 1));
 
         m_triangles.Add(rstep 
            + zstep * (rmax +  1) + 1);
        m_triangles.Add(rstep 
            + zstep * (rmax + 1) + (rmax + 1) + 1);
        m_triangles.Add(rstep 
             + zstep * (rmax + 1) + (rmax + 1));
    }
}

How to map the tiles as texture

The mapping of the tiles as a texture is actually rather simple. But first I would like to get back at why we are using patches and not a sphere.

Texture mapping in WPF is done by providing a collection of 2D points which are positions in the bitmap used as the texture. The position of each coordinate in this collection is used to know to which 3D point of the geometry it maps. The first 2D point in the texture coordinate collection maps to the first 3D point of the geomatry collection. The second to the second, the third to the third, etc…

This gives us two options for applying the mapping:

  1. Either we create a complete sphere and construct one big bitmap representing the world and map it onto this sphere.
  2. Or we create multiple patches which each correspond with a tile from google, map the tile on the patch, and collect all the patches into one view.

We will use the second option because the first one will get us into trouble when using other zoomlevels from google maps. When zooming in on the world in google maps, each tile from the previous zoomlevel gets split into 4 new tiles (see the How Google Map Works article). The number of tiles for each zoomlevel starts at 1, then 4, 16, 64, 256, etc… Imagine we wanted to create a bitmap of the world at the closest zoomlevel: we’d have to download 2 * 2^17 tiles to construct our bitmap (17 being the maximum zoomlevel from google maps), which would then have enormous dimensions with each tile being a square of 256 * 256 pixels.

The mapping itself is basically reconstructing the mercator coordinates from the spherical coordinates. Fortunately I allready implemented this for my Offline Maps application.

So, without any further ado: here is the code

If the application is run, you see the following:

The Code

You can download the sample code from this post here

Links

[1] Windows Presentation Foundation (WPF) 3D Tutorial
[2] 3D Tools for the Windows Presentation Foundation
[3] Generating a sphere-mesh in XAML
[4] Texture Coordinates and Visuals on 3D surfaces
[5] How Google Map Works
[6] Wikipedia’s: Spherical coordinate system

About these ads

One Response to “WPF 3D: Google maps on a sphere, or ‘The World’”

  1. Mike Says:

    Hello, this did not work. When run it doesn’t produce any errors but the globe is not displayed. Any idea why? thanks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: