Direct output
In correspondence with the eagle-eye result, we expect to
dynamically update the related part of the map on the right hand of the web
page-magnifying the corresponding part of the real image in the map view area. We
want to put part of the factual map (big.jpg) into the viewing area. To output
part of the map, a new ASP.NET Web form named pic.aspx should be added, which
will output the corresponding image through the parameters-position and size. The
crucial code snippet of code-behind for page pic.aspx is shown in listing 2.
Listing 2 –partial code for Pic.aspx.cs
//……'using' clauses omitted here
namespace MapViewer
{
public class Pic : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
//Put the user-code here for initialization
//Give the path, output postion and size of the image
string strLocalPath = Server.MapPath("image/big.jpg");
int iLeft = 0, iTop = 0, iWidth = 100, iHeight = 100;
if (Request.QueryString["l"] != null)
{
iLeft = Convert.ToInt32(Request.QueryString["l"]);
}
if (Request.QueryString["t"] != null)
{
iTop = Convert.ToInt32(Request.QueryString["t"]);
}
if (Request.QueryString["w"] != null)
{
iWidth = Convert.ToInt32(Request.QueryString["w"]);
}
if (Request.QueryString["h"] != null)
{
iHeight = Convert.ToInt32(Request.QueryString["h"]);
}
//load the image
System.Drawing.Image image = new System.Drawing.Bitmap(strLocalPath);
//target region
Rectangle destRect = new Rectangle(0, 0, iWidth, iHeight);
//the initial image region
Rectangle srcRect = new Rectangle(iLeft, iTop, iWidth, iHeight);
//create a new Graphics object
Bitmap newImage = new Bitmap(iWidth, iHeight);
Graphics g = Graphics.FromImage(newImage);
g.SmoothingMode = SmoothingMode.HighSpeed;
//image output quality
g.CompositingQuality = CompositingQuality.HighSpeed;
//output to newImage object
g.DrawImage(image, destRect, srcRect, GraphicsUnit.Pixel);
//release graphics object
g.Dispose();
// release corresponding image resources
image.Dispose();
//output our image via binary stream
using (MemoryStream ms = new MemoryStream())
{
//image format is specified as Jpeg
newImage.Save(ms, ImageFormat.Jpeg);
//clear all output in the buffer stream
Response.ClearContent();
//set HTTP MIME type of the output stream to"image/Png"
Response.ContentType = "image/jpeg";
//output binary stream of the image
Response.BinaryWrite(ms.ToArray());
}
//release corresponding image resources
newImage.Dispose();
//Output
Response.Flush();
Response.End();
}
//……
Here, among the parameters passed to pic.aspx, (l,t)
represents the upper left of the image inside the whole big map, while (w,h)
refers to the width and height of the image output.
With the dragging mouse coordinates, we can easily figure
out the partial image in the viewing area. Thus, we add the following code (see
Listing 3) into the client side of maps.aspx.
Listing 3
window.onload = function()
{
if (document.all || document.getElementById)
{
oThang = document.all ?
document.all["thang"] : document.getElementById("thang")
oHandle = document.all ?
document.all["handle"] : document.getElementById("handle")
Drag.init(oHandle, oThang, -250, -105, -250, -158);
oThang.onDragEnd = function(x, y)
{
refreshMap(x + 250, y + 250);
};
}
// Update map show
function refreshMap(x, y)
{
oldX = x;
oldY = y;
//asynchronously invoke AJAX method
MapViewer.Maps.GetMapInfo(x, y, GetMapInfo_callback);
}
}
Well, so far, whenever we move the eagle-eye the partial
area behind the web page it will be immediately updated, as well as show that area
and the corresponding eagle-eye are consistent.
Rasterizing the output
The above outputting method is easy to achieve, while with
even a short distance moving you have to update the whole content of the map.
Then here arises a question-how to decrease the outputting quantity of the
image? The approach introduced here is to divide the whole map into small
square blocks and render by blocks. This way we can make full use of the
buffering capacity in the server side and client site so as to reduce the quantity
of repeatedly downloaded pictures with small quantities of map moving. So far,
experienced readers may have realized the Buzz Word-AJAX. Quite right, but we
will discuss details it later on.
First concentrate on dissecting the image, as is illustrated
in Figure 3 below.
Figure 3 –The corresponding position of the viewing
area in the whole map.
Seen from Figure 3, there are total four cases (illustrated by
A, B, C and D respectively) to be noticed, as listed below:
Case A: The upper left corner of the show area is just the
vertex of the map grid, which is the simplest case and we just need output the
total block of picture.
Case B: The top edge of the show area just aligns with the
horizontal line of the map grid, under which case other parts of the image are
integer number of blocks except for the left and right areas.
Case C: The left edge of the show area just aligns with the
vertical line of the map grid, under which case other parts of the image are
integer number of blocks except for the top and bottom areas.
Case D: This is the most complex case, under which case the
left edge of the show area aligns with neither the vertical nor the horizontal
line of the map grid. So we need to deal with the four edge areas while
internal parts of the image are integer number of blocks.
To adopt such a policy to output image by blocks is because
we can optimize the performance of the system thanks to the buffering mechanism
supplied by ASP.NET 2.0. According to the idea suggested above, we can put our
project into action from two aspects- the server side and the client side.
Server side:
In this sample we use a famous open source framework –
AjaxPro.NET. Above all, we should add reference to AjaxPro.dll to the project (for
related details, see the accompanying downloaded guide titled "A Quick
Guide How to Start.doc"). With this done, let us start to modify the
crucial configure file for ASP.NET 2.0-Web.config, as shown below.
Listing 4
//……(omitted)
<system.web>
<httpHandlers>
<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory, AjaxPro" />
</httpHandlers>
……(omitted)
</system.web>;
Moreover, we should do something to register the current
page class.
Listing 5
private void Page_Load(object sender, System.EventArgs e)
{
//Put the user-code here for initialization
Utility.RegisterTypeForAjax(typeof(Maps));
}
To describe properties such as the position and URL of the
image, we provide an ImageInfo class.
Listing 6
//ImageInfo class—responsible for small pieces of pictures
public class ImageInfo
{
///URL of the image
private string _url;
public string URL
{
get
{
return _url;
}
set
{
_url = value;
}
}
///x coordinate of the picture’ upper left corner
private int _left;
public int LEFT
{
get
{
return _left;
}
set
{
_left = value;
}
}
///y coordinate of the picture’ upper-left corner
private int _top;
public int TOP
{
get
{
return _top;
}
set
{
_top = value;
}
}
//ID identity to mark the small image
private string _id;
public string ID
{
get
{
return _id;
}
set
{
_id = value;
}
}
}
Next, on the server side we begin to create AJAX method named GetMapInfo (int x, int y) with parameters x and y representing the x and
y coordinates of the eagle-eye in the thumbnail image, respectively. Here, with
parameters x and y, we can easily figure out the corresponding show region
inside the whole map and decide which case (i.e. Case A, B ,C and D) it belongs
to and return the relevant image information. Here is the crucial code snippet
for GetMapInfo.
Listing 7
///Returns URL and pos info of the small image to client side
/// </summary>
/// <param name="x">x coordinate of the eagle-eye's upper left corner in the thumbnail image</param>
/// <param name="y"> y coordinate of the eagle-eye's upper left corner in the thumbnail image </param>
/// <returns>information of the small image </returns>
[AjaxMethod()]
public ImageInfo[] GetMapInfo(int x, int y)
{
//size of the raster grid
int iStep = 100;
//returned map info
ImageInfo[] map = null;
int iReminderX = (x * 5) % iStep;
int iReminderY = (y * 5) % iStep;
//According to the remainder, we calculate under four cases
if (iReminderX == 0)
{
if (iReminderY == 0) //x and y directions are both integral
{
//……(omitted)
}
}
return map;
}
The code of the method GetMapInfo is so long that we have to
cut it down. For detailed analysis of the whole code, please see the downloaded
source code.
Client side:
Here we can make full use of the AJAX technique to update
the show area in real-time via asynchronous AJAX invocation. The main idea
behind this lies in the absolute location of the <img> object, with which
we can piece together small scraps of images into a bigger one. Since the
object ImageInfo returned from the server side has already included necessary
information, such as coordinates and URL of the image, we can conveniently
update the show area with methods provided by DOM. On the other hand,
information of the map coordinates is also provided on the client side for
quickly locating so that the user can see the absolute mouse position in
real-time. Here is the main code in the client side.
Listing 8- SQL clause for creating the Mapinfo
table.
// Update map show
function refreshMap(x, y)
{
oldX = x;
oldY = y;
//asynchronous Ajax method
MapViewer.Maps.GetMapInfo(x, y, GetMapInfo_callback);
}
// Callback, repaint the map region with returned map info
function GetMapInfo_callback(response)
{
// Imageinfo object array
var arImageInfo = response.value;
//if successfully get ImageInfo array
if (arImageInfo)
{
// <div> object for map show region
var div = document.getElementById("map");
//delete the old pictures
while (div.childNodes.length > 0)
{
div.removeChild(div.childNodes[0]);
}
//iterate through every picture info
for (var i = 0;i < arImageInfo.length;i++)
{
//pre-buffering the picture using JavaScript's image object
var image = new Image();
image.src = arImageInfo[i].URL;
}
for (var i = 0;i < arImageInfo.length;i++)
{
//Create a new <img> object
var img = document.createElement("img");
// URL of the image
img.src = arImageInfo[i].URL;
//image show pos
img.style.position = "absolute";
img.style.left = 400 + arImageInfo[i].LEFT;
img.style.top = 80 + arImageInfo[i].TOP;
//……
img.onmousemove = function()
{
//Calculate mouse ordinates on the whole map
//Note, we should allow for page scrolling
var x = event.x + document.body.scrollLeft - 400 + 5 * oldX;
var y = event.y + document.body.scrollTop - 80 + 5 * oldY;
//show mouse pos ordinates when moving the mouse
ShowPosInfo(x, y);
}
// append the image into <div>object
div.appendChild(img);
}
}
}
// show ordinates info in status bar
function ShowPosInfo(x, y)
{
window.status = "x = " + x + ", y = " + y;
}
//……(omitted)