Overview  |  API  |  Data Structures  |  Parameters  |  Example Map

TerraService Example Map

Version 2.0 of the TerraService is now available! Version 2.0 supports the Urban Area data theme. Click on Version 2.0 changes to learn the specific changes required in your application to use Version 2.0.

Building an Image from TerraServer Tiles

Image formed by an application calling the TerraService Web Service methods The most common task is to "stitch together" several image tiles from TerraServer image database and create a new image cropped to some end user specified dimensions. The image on the right depicts image cropped from six separate TerraServer image tiles. The blue lines identify the pixels contributed by each separate tile. The following is the how this image was created using TerraService method calls. All TerraService method calls are in bold text.

The first step is to allocate a TerraService object and call the one of the GetArea methods to identify the TerraServer image tiles that are required to contribute to our image. For this example, we selected the GetAreaFromPt method because we wanted an image exactly 400 pixels wide by 200 pixels tall. Before we allocate the TerraService and call the GetAreaFromPt method, we allocate and assign all the parameters required by the GetAreaFromPt method.

	LonLatPt center = new LonLatPt();
	Int32 theme     = new Theme();	// Version 2.0 change to int
	Scale scale     = new Scale();
	Int32 imageWidth,  mapWidth;
	Int32 imageHeight, mapHeight;

	center.Lon = -121.821441650390063;
	center.Lat = 37.698287963867188;
	theme = 1;		// Version 2.0 change to integer value from Theme.Photo
	scale = Scale.Scale8m;
	mapWidth = 400;
	mapHeight = 200;
	TerraService ts = new TerraService();
	AreaBoundingBox abb = ts.GetAreaFromPt(center, theme, scale, mapWidth, mapHeight);
	

At this point, the abb structure is filled in with the "bounding box" of tiles we need to construct our new image. Now we need to create a new image in the dimensions we want, e.g. 400 by 200 pixels. Note, there are no calls to TerraService functions. We are using the interface to the Windows GDI calls provided by the .NET Common Language Runtime.

	// Construct the bitmap represeting the full composite image
	PixelFormat pf = PixelFormat.Format32bppRGB;
	Image compositeImage = new Bitmap(imageWidth, imageHeight, pf);
	Graphics compositeGraphics = Graphics.FromImage(compositeImage);

The next stretch of code calls GetTile method to fetch each image tile from TerraServer database. The code extracts the X and Y values from the abb.NorthWest AreaCoordinate structure. These values are used to iterate over the range of image tiles to extract from the TerraServer database. The X value in the abb.NorthEast AreaCoordinate structure bounds the first for loop. The Y value in the abb.SouthWest AreaCoordinate structure bounds the inner for loop. Inside the inner for loop, a new TileId tid variable is created and assigned to the x and y values in the for loops. The call to GetTile fetches the tile from the TerraServer database and returns it in its compressed form. The calls to "MemoryStream", and "Image.FromStream" decompress the image tile. The call to "compositeGraphics.DrawImage" places the tile from TerraServer at a specific location within the new 400 by 200 pixel image we are constructing. The two lines following "compositeGraphics" depict how the cropping works. The new image is placed into its relative position by the within the composite image — "(x - xStart) * tileImage.Width". This location is adjusted by the offset value found in the abb.NortWest.Offset AreaCoordinate structure. The same calculation is done for the Y offset.

	Int32 xStart = abb.NorthWest.TileMeta.Id.X;
	Int32 yStart = abb.NorthWest.TileMeta.Id.Y;
	for (Int32 x = xStart; x <= abb.NorthEast.TileMeta.Id.X; x++) {
		for (Int32 y = yStart; y >= abb.SouthWest.TileMeta.Id.Y; y--) {
			TileId tid = abb.NorthWest.TileMeta.Id;
			tid.X = x;
			tid.Y = y;
			Image tileImage = Image.FromStream(new MemoryStream(ts.GetTile(tid)));
			compositeGraphics.DrawImage(tileImage, 
				(x - xStart) * tileImage.Width  - (Int32) abb.NorthWest.Offset.Lon,
				(yStart - y) * tileImage.Height - (Int32) abb.NorthWest.Offset.Lat, 
				tileImage.Width, tileImage.Height);
			tileImage.Dispose();
		}				
	}

The above code has now produced a cropped image from a set of TerraServer image tiles. We can draw on top of the image to produce the blue grid lines.

	// Draw the tile grid lines
	if (gridWidth != 0) {				// only do it if the line width is > 0
		Int32 gridWidth    = 3;
		Color gridColor    = Color.FromARGB(Int32.Parse("800000ff", NumberStyles.HexNumber | NumberStyles.AllowHexSpecifier));
		Pen pen = new Pen(gridColor, gridWidth);
		for (Int32 x = xStart; x <= abb.NorthEast.TileMeta.Id.X; x++) {
			Int32 xCoord = (x - xStart) * 200 - (Int32) abb.NorthWest.Offset.Lon;
			compositeGraphics.DrawLine(pen, new Point(xCoord, 0), new Point(xCoord, mapHeight));
		}
		for (Int32 y = yStart; y >= abb.SouthWest.TileMeta.Id.Y; y--) {
			Int32 yCoord = (yStart - y) * 200 - (Int32) abb.NorthWest.Offset.Lat;
			compositeGraphics.DrawLine(pen, new Point(0, yCoord), new Point(mapWidth, yCoord));
		}
	}

The next step is to insert our USGS logo so that end users know the image is provided by the US Geological Survey. The methods to do this are provided by the .NET Common Language Runtime. There are no TerraService methods required to perform this function. The computations cause the logo to appear in the lower right hand corner of the image indented five pixels from the right and bottom edges.

	try {
		Image logoImage = Image.FromFile(@"c:\images\usgswtrmk.jpg");
		compositeGraphics.DrawImage(logoImage, 
			mapWidth  - logoImage.Width  - 5,
			mapHeight - logoImage.Height - 5, 
			logoImage.Width, 
			logoImage.Height);
	}
	catch {}

Finally, we need to compress the image and output it to the client. The methods to do this are provided by the .NET Common Language Runtime. There are no TerraService methods required to perform this function.

	context.Response.ContentType = "image/jpeg";
	compositeImage.Save(context.Response.OutputStream, ImageFormat.JPEG);//, encoderParameters);
	compositeImage.Dispose();
	context.Response.Flush();

This code presumes it was called from within the context of a Active Server Page+ web handler class. The Active Server Page+ environment passes the context object to the web handler object. It is the context object that provides access to the data stream to client's web browser. Thus, the beauty of this example is no disk resources were used in the construction and transmission of the tiles from the TerraServer database to the client's web browser.

back buttonhome page buttonnext page button