Posts tagged ‘Terrain’

July 20, 2011

Creating Hill Shaded Tile Overlays

The default Bing Maps road style uses a “hillshade” effect to give an impression of underlying terrain. It’s a relatively subtle, but surprisingly powerful technique to enhance the appearance of map layers, as demonstrated by comparing the following two tiles:

image
Without hillshading

image
With hillshading

In this post, I’ll describe how to create your own hillshade overlay from digital elevation model (DEM) data, using the GDAL toolset.  By creating the overlay as a set of semi-transparent tiles, rather than pre-rendered into the tiles as shown above, you can place them on top of any Bing Maps/Google Maps et al. tilelayer to represent the underlying terrain.

The process I’ve followed is based on the work of others, most notably PerryGeo, and you can find some other guides on the internet to achieve this same effect. However, I found some of the existing guides on the subject to be either out-of-date or require knowledge of Linux BASH commands etc., so I hope that some of you will find this new step-by-step guide helpful.

1.) Acquire a DEM terrain model

To start with, you’re going to need some source data about the underlying terrain of the earth from which to calculate your hillshade. There’s lots of places to acquire this data from; Perhaps the easiest to use (assuming you’ve got Google Earth installed) is to open the kmz file available from http://www.ambiotek.com/topoview. This uses Google Earth as a graphical interface for v4.1 of the  elevation dataset gathered by the Shuttle Radar Topography Mission (SRTM), from which you can click to download individual DEM tiles covering 5°x 5°, as shown below:

image

Alternatively, you can access these files directly from the KCL server (my former university, incidentally) at http://srtm.geog.kcl.ac.uk/portal/srtm41/srtm_data_geotiff/

The data is provided in GeoTIFF format. You can load one of these tiles up in any graphics program that can load TIFF files, but it won’t look very interesting yet. The height information is encoded in additional metadata that will be ignored by normal graphics programs, so you’ll probably just get an image like this (this is srtm_36_02.tif):

image

Black parts show the presence of data in the underlying file, which we’ll subsequently process using GDAL tools to create shaded images.

2.) Reproject to Spherical Mercator

Most DEM data sources, including the SRTM data I linked to above, are provided in Plate Carree projection – i.e. WGS84 coordinates of longitude are mapped directly to the x axis of the image, while latitude is mapped directly to the y axis. Before we create tiles from this data suitable for overlay on Bing Maps, Google Maps, et al. we therefore need to transform it into the Spherical Mercator projection. You can do this using gdalwarp, as follows:

gdalwarp -dstnodata 0 -tr 305.7481 305.7481 -multi -co "TILED=YES" -t_srs EPSG:3857 srtm_36_02.tif srtm_36_02_warped.tif

The full list of parameters accepted by gdalwarp are listed here,  but the options I set are as follows:

  • dstnodata states what value to use to represent nodata values (the equivalent of null in a SQL database, for example). I’ve set a value of 0 (i.e. black).
  • tr gives the target resolution in x and y dimensions. The SRTM data I’m using was recorded at 90m resolution, so you might think that this should be set to 90 90. However, I’m going to be using this data for display on Bing Maps at different zoom levels, which will necessarily involve resampling the image.  Therefore, you should set this value to the resolution (in metres/pixel) of the maximum zoom level on which you plan to overlay your data. (Remember that maximum zoom level will have the smallest resolution). You can obtain this value from my Bing Maps Ready Reckoner. In the case above, I’m planning overlaying my data on Zoom Level 9 and above, so I set a value of 305.7481 (in both dimensions). If I’d wanted to go to Zoom Level 10, I would have decreased this to 152.87 instead.
  • multi allows parallel processing
  • co “TILED=YES” is a format-specific option that states that the output TIFF file should be tiled rather than stripped (see http://www.fileformat.info/format/tiff/egff.htm for an explanation of the difference)
  • t_srs gives the destination spatial reference system into which the image should be reprojected. In this case, EPSG:3857, as used by Bing Maps, Google Maps etc.

The resulting image, srtm_36_02_warped.tif, will still be a GeoTIFF file, but will now be projected as follows. The height and width of the output image will depend on the target resolution you specified in the tr parameter:

image

3.) Convert from DEM to Hillshade

The warped GeoTIFF file has height data encoded in it, but we want to translate that information into a visible shaded effect, and for this we can use gdaldem.

gdaldem actually provides several interesting functions related to working with DEM data, including the ability to derive contour lines, and create shaded relief maps. Maybe I’ll write about these another time, but for this example we want to use the hillshade mode. You can shade the warped image created in the previous step as follows:

gdaldem hillshade srtm_36_02_warped.tif srtm_36_02_warped_hillshade.tif -z 2 -co "TFW=YES"

This time, I’m only supplying two additional parameters:

  • z is a scaling factor applied to the generated hillside image that accentuates the hills, increasing the contrast of the image. I provided a value of 2 just to enhance the effect a bit, but you might decide you don’t need this.
  • co “TFW=YES” specifies that the output image should be created with an accompanying “world file”. This is a simple ASCII text file that provides additional information about the geographic extents of the created image, which we’ll need to use in a later step to line the hillshade image up with the Bing Maps tiling system. You can look up more information about world files on wikipedia.

There are additional parameters that allow you to specify the direction and the angle of the light source from which the simulated shadows will be created.

The result of executing the above code will be another TIFF file, in which the background is black, and the elevation data from the DEM has been converted into shades of grey, as follows:

image

At this stage, you could stop if you wanted to, and simply create a tile layer from the hillshaded image, which would look a bit like this:

image

Which makes the landscape of North Wales look a bit like the Moon, I think…

To make the data slightly more usable, we need to carry on with a few more tweaks.

4.) Making a Semi-Transparent Overlay

Currently, our hillshade image is opaque, with the shadows cast by terrain represented by variations in brightness of the colour used. To make this into an re-usable overlay that can be used on top of other layers, we need to make the image semitransparent, with shadows cast by terrain being represented by variations in opacity instead.

There are several ways of modifying the image data to achieve this effect. You could do it in Photoshop or another graphics program, for example, or using the graphics libraries in C# or PHP. Since I’m currently trying to learn Python, and GDAL is quite closely linked with Python, I’ll try to do it using the Python Imaging Library instead.

The following Python script makes a number of tweaks to the image above. Firstly, it converts it to a pure greyscale image (while the image above looks greyscale, it’s actually using a colour palette). It then inverts the image, turning it into a negative image. The reason for the inversion is that we then copy the (single) channel of the greyscale image into the opacity channel of a new RGBA image – areas that were very light in the source want to have very low opacity in the transparent image, and vice-versa, so the channel needs to be inverted.

Finally, we scan through the data to find instances of pixels that are pure black (RGBA value 0, 0, 0, 255) –this was the nodata value we set in step one – and replace them with pure transparent pixels (0, 0, 0, 0). The alpha channel in the tuples of any other pixels is also lightened slightly – I chose a value of 74 somewhat arbitrarily because I thought the resulting image looked good – you can choose whatever value you want, or none at all.

from PIL import Image as PImage
from PIL import ImageOps

# Load the source file
src = PImage.open("srtm_36_02_warped_hillshade.tif")

# Convert to single channel
grey = ImageOps.grayscale(src)

# Make negative image
neg = ImageOps.invert(grey)

# Split channels
bands = neg.split()

# Create a new (black) image
black = PImage.new('RGBA', src.size)

# Copy inverted source into alpha channel of black image
black.putalpha(bands[0])

# Return a pixel access object that can be used to read and modify pixels
pixdata = black.load()

# Loop through image data
for y in xrange(black.size[1]):
  for x in xrange(black.size[0]):
    # Replace black pixels with pure transparent    
    if pixdata[x, y] == (0, 0, 0, 255):
      pixdata[x, y] = (0, 0, 0, 0) 
    # Lighten pixels slightly
    else:
      a = pixdata[x, y]
      pixdata[x, y] =  a[:-1] + (a[-1]-74,)

# Save as PNG
black.save("srtm_36_02_warped_hillshade_alpha.png", "png")

(Much of the logic in this script came from here). The resulting image will be a PNG file, in which darker shadows are represented by increasingly opaque black parts, while lighter shadows are more transparent:

image

5.) Cut into Tiles

Now, to cut the PNG image into tiles. The only problem is that, having converted from a TIF to a PNG in the Python script above, we have lost the geo-referencing information that, up to that point, had been stored in the GeoTIFF metadata associated with the image. PNG files only contain image data, so we need to provide the associated geo information in a separate world file. Fortunately, (assuming you haven’t done any cropping, rotating, or resampling in the meantime), the PNG file above still (geographically) matches the hillshade TIFF file created in step 3, which, as you’ll recall, we created with an associated world file. So, all we need to do is take a copy of that world file and associate it with the PNG file instead:

copy srtm_36_02_warped_hillshade.tfw srtm_36_02_warped_hillshade_alpha.pngw

Naming the file with exactly the same filename as the PNG image file itself but with the .pngw extension means that GDAL will automatically pick up the associated geo-information when it tries to use the PNG file.

The final step, then, is just to use the gdal2tiles.py script to cut the PNG image into tiles:

python gdal2tiles.py srtm_36_02_warped_hillshade_alpha.png --s_srs EPSG:3857

Let it whir away until you’ve got directories of tiles stored in the TMS z/x/y structure:

image

And then you can create a new TileLayer that points to this set of tiles and add it to your map, on top of any other layer. As I said right at the very top of this article, the default Bing Maps road style already has hillshading effects added, so there’s not much point overlaying this tile layer on top of the road style. But here’s what it looks like placed on top of some other map styles (click to enlarge):

Open Street Map

image
Without hillshading overlay
image
With hillshading overlay

Ordnance Survey MiniScale

image
Without hillshading overlay
image
With hillshading overlay
January 6, 2011

Examining 3D Terrain of Bing Maps Tiles with SQL Server 2008 and WPF (Part 3)

In the previous post in this series, I selected a Bing Maps tile and converted its quadkey to a POLYGON representing the geographic extent of that tile. I then used that POLYGON as the basis for a query of the GTOPO data in SQL Server to retrieve the corresponding elevation data for the terrain on the tile. In this post I’ll combine these two into a 3d model showing the terrain of that tile.

Triangulating the Elevation Data

The GTOPO data is a set of distinct elevation recordings, recorded on a grid spaced at regular intervals, like this:

image

In order to construct a 3d terrain model from this elevation data, we need to construct a continuous smooth surface from those distinct points. For this, we will use triangulation.

Tringulation, as its name suggests, is the process of creating triangles from a set of data points. These triangles will tesselate together to cover the entire extent of data, with no gaps and no overlaps. There are many different triangulations of the same set of points – but I’ll use the Delauney Triangulation. I could probably write a whole new blog post on the Delauney triangulation, but for now all I’ll say is that there are many examples of code on the internet that describe how to create a set of triangle polygons from an input set of points. For example, you could try looking here, here, or here.

Triangulating the set of elevation points leads to a set of triangular polygons, like this:

image

At first glance it may not be obvious why having a set of triangles in which each vertex is a point in the dataset is any better than the set of points we started with. What you’ve got to remember (and what the spatial results tab in SQL Server Management Studio can’t show you) is that each of the vertices of these triangles has an associated Z value – they are all at different heights. Therefore the triangles in our dataset now form a set of connected, angled faces.

Creating the WPF 3D Mesh

To display a 3D mesh representing the surface constructed from these triangular faces, I’ll use a WPF MeshGeometry3D object. First, connect to the database and loop through the triangulated SqlGeometry polygons, adding the X, Y, and Z values to the Mesh Positions array:

MeshGeometry3D mesh = new MeshGeometry3D();


while (dataReader.Read())

{

  SqlGeometry v = (SqlGeometry)dataReader.GetValue(0);

  for (int n = 3; n >= 1; n—)

  {

    mesh.Positions.Add(new Point3D(

      (double)v.STPointN(n).STX,

      (double)v.STPointN(n).Z / 10000,

     -(double)v.STPointN(n).STY

    ));

  }

}

There’s a couple of points to note here:

  • Firstly, every value returned by my DataReader is a triangular SqlGeometry polygon. Polygons must be closed, so they actually contain 4 points – the last point being the same as the first point. However, we only want the three distinct vertex locations.
  • Secondly, when defining 3D objects in WPF (as in most 3D applications), the order in which you define the coordinates is important as this is used to determine the “direction” in which each face points. Unless you explicitly specify vertex normals, you should define vertices in anticlockwise order as you look at them to ensure that the associated face points towards you  Faces are single-sided, so if you look at them from behind they will appear invisible. I loop through the points of each triangle from n = 3 to n = 1 to ensure they are added to the mesh in anticlockwise order.
  • I’ve applied an arbitrary scaling factor of 10,000 to the Z value just to make the mountains and valleys look aesthetically attractive compared to the horizontal dimensions. I wouldn’t have needed to do this if I had first projected my latitude and longitude coordinates into metres, so that they were measured in a unit consistent with the GTOPO elevation data.
  • Notice that, when considering a flat two-dimensional object, we tend to think of the x axis as extending across towards the right of the screen, and the y coordinate coordinate extends up the screen. The z coordinate comes towards us from the screen. However, the WPF 3d coordinate system treats x coordinate as extending to the right, the z coordinate extending upwards, and the y coordinate extending forwards. So, to map the SqlGeometry coordinate values to the WPF Point3D object, I use X = X, Y = Z, and Z = –Y.

The resulting mesh, illustrating the mountains and valleys of Scotland contained on this tile, looks like this:

image

Adding Material to the Mesh

Now we’ve got our mesh, we can specify a material to paint it with. But, before we do, we need to specify texture coordinates for each point of the mesh. Texture coordinates are used to describe how a 2D image should be stretched over a 3D shape. Fortunately, because we’re dealing with a square tile, the texture coordinates are thankfully simple – we want to stretch the entire image over the entire mesh, with the image equally stretched at every point.

Thus, for each point added to the mesh, the associated texturecoordinate is as follows:

mesh.Positions.Add(new Point3D(x, z, -y));

mesh.TextureCoordinates.Add(new Point(x, -y));

Once we’ve specified the appropriate texturecoordinates for each point in the mesh, we can create a material based on an ImageBrush to superimpose the original Bing Maps tile image over the top.

ImageBrush imgBrush = new ImageBrush();

imgBrush.ImageSource = new BitmapImage(new Uri(@"http://ecn.t2.tiles.virtualearth.net/tiles/r031133.png?g=603&mkt=en-us", UriKind.Absolute));

Material mat = new DiffuseMaterial(imgBrush);

this.meshGeometry.Material = mat;

Here’s what the terrain mesh looks like with an aerial tile image brush:

image

Or, with a road tile:

image

Smoothing the Mesh

Currently, my mesh lists each point of each triangle separately – it contains 2,099 faces and 6,297 vertices (3 distinct vertices for each triangle). However, many of the vertices in the mesh are actually duplicates – any vertex at which two or more triangles meet is currently listed multiple times. The effect of listing points in this manner is to cause each triangle to be rendered individually, causing the “hard edges” between faces shown in the preceding image.

As an alternative, we could list only unique points in the Positions list of the mesh, and then use the TriangleIndices property to list the index positions of each vertex that forms a triangle. For example, when looping through each point, x:

int x = mesh.Positions.IndexOf(p);

// If this point is not already in the mesh

if (x == -1)

{

  mesh.Positions.Add(p1);

  mesh.TextureCoordinates.Add(p.X, -p.Y));

  mesh.TriangleIndices.Add(mesh.Positions.Count - 1);

}

// If it already exists

else

{

  mesh.TriangleIndices.Add(x);

}

The effect this has is to smooth the mesh, as follows:

image

And, when overlaid with a road tile:

image

Tags: , , ,
December 26, 2010

Examining 3D Terrain of Bing Maps Tiles with SQL Server 2008 and WPF (Part 2)

In the last post, I downloaded some elevation data from the GTOPO digital elevation model and loaded it into a SQL Server table. In this post, I’m now going to select a subset of that data in order to create a terrain map of a particular Bing Maps tile.

The Bing Maps Tile System

As you probably already know, Bing Maps are constructed from a set of raster “tiles” – prerendered 256px x 256px gif, jpg, or png images that form the background of each map. There are different sets of tiles that correspond to the different styles of map.

Here’s a few examples:

r120200223312

Road Map Tile

h120200223312

Aerial Map Tile

r120200223312

Ordnance Survey Map Tile

Every tile is numbered with a quadkey – a unique identifier that exactly describes the position and zoom level of where that tile should be placed. I’m not going to explain the quadkey system here, because there’s already an excellent article on MSDN.

The important fact is that, by knowing only the quadkey of a tile, it is possible to determine the exact coordinate boundaries of every corner of that tile. In SQL Server, that means we can construct a POLYGON that represents the extent of that tile, and retrieve any data that lies on the tile (or, in this example, the elevation data that corresponds to the terrain of that tile). The following method demonstrates how to return a SqlGeography polygon from a Bing Maps quadkey:

public static SqlGeography GeographyPolygonFromQuadKey(string quadKey) {

      int tilex = 0, tiley = 0;

      int tilewidth = 256, tileheight = 256;

      int zoomLevel = quadKey.Length;

      // Work out the x and y (grid) position of this tile

      for (int i = zoomLevel; i > 0; i--)

      {

        int mask = 1 << (i - 1);

        switch (quadKey[zoomLevel - i])

        {

                case '0':

                    break;

                case '1':

                    tilex |= mask;

                    break;

                case '2':

                    tiley |= mask;

                    break;

                case '3':

                    tilex |= mask;

                    tiley |= mask;

                    break;

                default:

                    throw new ArgumentException("Invalid QuadKey digit sequence.");

            }

        }

 

        // From the grid position and zoom, work out the min and max Latitude / Longitude values of this tile

        float minLongitude = (float)(tilex * tilewidth) * 360 / (float)(tilewidth * Math.Pow(2, zoomLevel)) - 180;

        float maxLatitude = (float)Math.Asin((Math.Exp((0.5 - (tiley * tileheight) / (tileheight) / Math.Pow(2, zoomLevel)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - (tiley * tileheight) / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;

        float maxLongitude = (float)((tilex + 1) * tilewidth) * 360 / (float)(tilewidth * Math.Pow(2, zoomLevel)) - 180;

        float minLatitude = (float)Math.Asin((Math.Exp((0.5 - ((tiley + 1) * tileheight) / (tileheight) / Math.Pow(2, zoomLevel)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - ((tiley + 1) * tileheight) / 256 / Math.Pow(2, zoomLevel)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;

 

        // Create a new SqlGeography instance representing the extent of this tile

        SqlGeographyBuilder gb = new SqlGeographyBuilder();

        gb.SetSrid(4326);

        gb.BeginGeography(OpenGisGeographyType.Polygon);

        gb.BeginFigure(minLongitude, maxLatitude);

        gb.AddLine(minLongitude, minLatitude);

        gb.AddLine(maxLongitude, minLatitude);

        gb.AddLine(maxLongitude, maxLatitude);

        gb.AddLine(minLongitude, maxLatitude);

        gb.EndFigure();

        gb.EndGeography();

 

      return gb.ConstructedGeography;

    }

 

Now, I just need to choose a particular tile whose terrain to examine…. I’ve chosen the tile at zoom level 6 that portrays most of Scotland – my ancestral home and also home to some interesting geographic features (i.e. mountains and lochs!). Here’s the tile viewed using Bing Maps’ road map style:

r031133

The quadkey for this tile is 031133 and, using the method above, I can calculate the corresponding SqlGeography as:

POLYGON((-5.625 55.7765730186677, 0 55.7765730186677, 0 58.8137417157078, –5.625 58.8137417157078, -5.625 55.7765730186677))

Therefore, we can retrieve the relevant GTOPO30 elevation data for this tile from SQL Server using the following query:

SELECT * FROM W020N90 WHERE geog4326.STIntersects(geography::STPolyFromText(‘POLYGON((-5.625 55.7765730186677, 0 55.7765730186677, 0 58.8137417157078, –5.625 58.8137417157078, -5.625 55.7765730186677))’, 4326) = 1;

Here’s the results:

image

In the next post, I’ll use this elevation data to create a 3d surface onto which to overlay the Bing Maps tile image.

Tags: , , ,
December 17, 2010

Examining 3D Terrain of Bing Maps Tiles with SQL Server 2008 and WPF (Part 1)

Following on from my last last post, I thought I’d continue to explore the topic of altitude (or elevation) and how it relates to Bing Maps.

A map is, by definition, two-dimensional – it is an image that has been projected onto a flat surface. However, it is still possible, and on many occasions very important, to portray three-dimensional properties of features on that map. One way of doing this is by presenting an oblique, “birdseye” view rather than a pure top-down view. This is certainly an effective way to present the height of individual distinct features such as buildings, as in the default Bing Maps control. But what about displaying variations in height of the underlying terrain? In this first of a set of posts I’ll look at a way of combining altitude data, SQL Server, and WPF to create a “3D” view of the elevation of terrain on a Bing Maps tile.

Obtaining Altitude Data

Bing Maps does not expose altitude data, so the first step is to acquire this data from somewhere else. There have been various scientific surveys of global elevation data, produced by government agencies and scientific organisations such as NASA. Three common sets of data are as follows:

  • GTOPO30 – global elevation grid at resolution of 30 arcseconds (approx 1km).
  • SRTM – elevation grid at resolution 3 arcseconds (approx 90m) between latitudes –56 and +60.
  • ASTER – elevation grid with resolution 1 arcsecond (approx 30m) between latitudes –83 and 83.

Elevation Webservices

The data from various elevation surveys is exposed via webservices provided by the USGS, Geonames and Google (amongst others). For example, to determine the altitude of the point located at (53.06839056444848, -4.076281785964964), you could call one of the following URLs

These services are very useful if you want to retrieve the elevation of only one, or a small number of points. However, for this example I’m going to use several thousand elevation points spread out across the surface of a Bing Maps tile, so these services aren’t really suitable.

Elevation Datasets

Each dataset is also available to download in full. I’ll be using the GTOP30 dataset, which you can download from http://eros.usgs.gov/#/Find_Data/Products_and_Data_Available/gtopo30_info. It’s split into several smaller tiles, so if you only want to download a particular area that’s fine. Tiles are named according to the coordinates at the top left corner. I’m going to use the W020N90 tile, which starts at a longitude of –20 and latitude of 90, and covers most of Western Europe, as highlighted below:

image

The W020N90 data is provided as a gzip archive, which is about 8.6Mb in size.

Converting and Loading into SQL Server

The GTOPO data is supplied as a DEM image, so the next step is to convert it into a more useable format. I’m going to use the excellent free MicroDEM program to crop a section of the W020N90 DEM file and save it as ASCII text file.

Converting the DEM Binary file to ASCII XYZ

  1. Load the DEM data into MicroDEM by selected File —> Open, and selecting the HDR header file from the GTOPO download.
  2. Once the data has loaded, use the Subset & zoom tool from the MicroDEM menu bar to select the part of the map to crop (if desired).
  3. Select File –> Save DEM –> Current subset, MD DEM.
  4. Close the current file and re-open the newly saved cropped file.
  5. Save the cropped file in text format by selecting File –> Save DEM –> ASCII XYZ.

image

Tidying Up the ASCII XYZ file

Once saved out as a ASCII XYZ file, the elevation data is stored as a very simple dataset with three columns as follows:

LAT        LONG        ELEVATION

52.837501  -8.479166   55
53.670834  -8.479166   77

The only slightly annoying thing about the XYZ export from MicroDEM is that there isn’t a consistent separator used to delimit the columns – it seems that any length of space is used as a separator. To fix this, I then opened up Visual Studio and performed a quick replace (Ctrl + H) using regular expressions:

  • First, to remove leading spaces on some of the lines, search for ^[ \t]+ and replace with an empty string.
  • Then, to replace the variable-length spaces between columns, search for [ ][ ]* and replace with ,

We’ve now got a nice comma-separated CSV file that is easy to import into SQL Server:

LAT,LONG,ELEVATION

52.837501,-8.479166,55
53.670834,-8.479166,77

Importing into SQL Server

Once you’ve imported the CSV file into a new table in SQL Server (Use the import/export wizard), we can add a geography column to that table, and populate it with a Point in each row. Since we want our Points to contain Z values, we have to use one of the WKT static methods (the geography Point() method, the xxxFromWKB() and GeomFromGml() methods can only be used to create two-dimensional coordinates):

ALTER TABLE W020N90

ADD GEOG4326 geography;

 

UPDATE W020N90

SET GEOG4326 = geography::STPointFromText('POINT(' + LONG + ' ' + LAT + ' ' + ELEVATION + ')', 4326);

(note that WKT requires longitude/latitude/altitude coordinate ordering, not latitude/longitude/altitude as you might expect).

At this stage, you can check the GTOPO elevation point data (well, the first 5,000 rows of it, anyway) by executing a SELECT query from the SQL Server table and checking the Spatial Results tab in SQL Server Management Studio, as shown below:

image

In the next post, I’ll examine how you can convert this table of points into a set of polygonal faces that can be used as the basis for a 3D terrain model for any particular geographic area.

Tags: , , ,
Follow

Get every new post delivered to your Inbox.

Join 53 other followers