// internal mousemove handler (supports IE, FF and NS)
// don't use VE onmousemove because it is broken in VE5 3D
if (!document.all && !document.getElementById)
{
document.captureEvents(Event,MOUSEMOVE);
}
document.onmousemove=MDNMouseMoveHandlerOverride;
// mouse hover drill-down
function MDNDrillDownReady(curMouseMapPos)
{
HandleDrillDownIdentify(curMouseMapPos.Longitude, curMouseMapPos.Latitude,
MDNGetVEMap().GetZoomLevel());
}
3. Enhance the code-behind (Default.aspx.cs)
First, add namespaces (if necessary):
using System.Drawing;
using ISC.MapDotNetServer.Controls.VirtualEarth.Version5; Next, we will override a base function RegisterTemplateScript which allows us to dynamically create javascript and register it on the client before the page has finished loading. Don't forget to call the base method.
#region Protected Methods
/// <summary>
/// Custom script to register.
/// This is dynamic script based on server-determined values and settings.
/// </summary>
protected override void RegisterTemplateScript(MapControlBridge mcb)
{
// call base
base.RegisterTemplateScript(mcb);
// scripts-----------------------------------------
string script = "<script type='text/javascript'>" +
// handler for identify query
"function HandleDrillDownIdentify(x, y, zl)" +
"{" +
"if (" + BlockingMutexName + "==0 && mdnDrillDownReady==true)" +
"{" +
BlockingFunctionName + "(true, 3000);" +
// uncomment this if you have ProgressImageID set on the MapControlBridge
//ShowProgressFunctionName + "(true);" +
"CallbackMethods.HandleDrillDownIdentify(x, y, zl, " + OnMapStateChangeCompleteCallbackName + ");" +
"}" +
"else if(!mdnDrillDownReady){window.setTimeout('mdnDrillDownReady=true;', 1000)}" +
"}" +
"</script>";
// register the client script
Page.ClientScript.RegisterClientScriptBlock(GetType(), "TemplateScript2", script);
}
#endregion
/// <summary>
/// Drill-down identify web method - used to query the appropriate layers as defined in the map file metadata
/// and highlight the feature and display attribute information.
/// </summary>
/// <param name="x">x position over map in lat/lon.</param>
/// <param name="y">y position over map in lat/lon.</param>
/// <returns>MapClientStateUpdate.</returns>
[ScriptMethod]
public MapClientStateUpdate HandleDrillDownIdentify(double x, double y, int zl)
{
// Wrap this whole method in a try/catch to handle any server-side
// exceptions gracefully
try
{
// Check for a session timeout and cancel execution
if (!MapControlBridge1.IsSessionTimedOut)
{
// Lock up this session
lock (LockObjectForAjax)
{
// build a string which will contain the javascript that is
// passed to the client browser
StringBuilder sb = new StringBuilder();
// wrap javascript to handle client-side exceptions gracefully
sb.Append("try{");
// Hide any previous info boxes
sb.Append(VEShape.GenerateHideInfoBoxScript(VEMap));
// convert from WGS84 to calculate a reasonable search area
Point mapPoint84 = new Point(x, y);
Point mapPointMerc =
Tiles.ConvertDegreesPointToMercMeters(mapPoint84);
// based on current mapscale get a reasonable fraction of the current display envelope
double screenSegMeters = Tiles.GetMetersPerPixByZoomLevel(zl, y) * 8.0;
// disable check of map scale
// for VE integrated apps, MapScale must be set manually
// see the code sample for a demonstration
MapQueryManager1.OnlyQueryInScaleLayers = false;
// setup parameters on MapQueryMananger
// Note that this code assumes the underlying data in the database
// is unprojected (WGS84 particularly). If your data is projected you will
// need to reproject this point to your correct projection.
// (see the Reference Library page "Transform" for an example)
MapQueryManager1.SpatialQueryShape = mapPoint84;
MapQueryManager1.SpatialQueryOperation = ShapeFilterOperations.DRILL_DOWN;
// the radius is based on 8 pixels
Point newPt84 =
Tiles.ConvertMercPointToDegrees(mapPointMerc.Offset(0, screenSegMeters));
MapQueryManager1.SpatialQueryBufferRadius = newPt84.Delta(mapPoint84);
// Execute the query
MapQueryManager1.ExecuteQuery();
// Grab the first shape we can find
if (MapQueryManager1.QueryResponse != null &&
MapQueryManager1.QueryResponse.QueryResultTables.Count > 0 &&
MapQueryManager1.QueryResponse.QueryResultTables[0].QueryResultRows.Count > 0)
{
// show first shape using VE
QueryResultRow row = MapQueryManager1.QueryResponse.QueryResultTables[0].QueryResultRows[0];
if (!string.IsNullOrEmpty(row.ShapeSerial))
{
// Provide a value for the VEShapeLayer
string vEHighlightLayer = "MyHighlightLayer";
// provide sensible default colors for highlight shape
Color fillColor = Color.FromArgb(128, 255, 0, 0);
Color outlineColor = Color.Red;
// get layer of feature being highlighted, and its highlighting metadata
Layer hLayer = MapQueryManager1.QueryRequest.Queries[0].Layer;
if (hLayer != null &&
hLayer.MetaData != null)
{
// These can be set int the mapefile in the METADATA section of the Layer:
// HighlightFillColor "80, 0, 255, 0"
// HighlightOutColor "80, 0, 255, 0"
string[] colorData;
if (hLayer.MetaData.ContainsKey("HighlightFillColor"))
{
colorData = hLayer.MetaData["HighlightFillColor"].Split(',');
if (colorData.Length == 4)
{
fillColor = Color.FromArgb(
Convert.ToByte(colorData[0]),
Convert.ToByte(colorData[1]),
Convert.ToByte(colorData[2]),
Convert.ToByte(colorData[3]));
}
}
if (hLayer.MetaData.ContainsKey("HighlightOutColor"))
{
colorData = hLayer.MetaData["HighlightOutColor"].Split(',');
if (colorData.Length == 4)
{
outlineColor = Color.FromArgb(
Convert.ToByte(colorData[0]),
Convert.ToByte(colorData[1]),
Convert.ToByte(colorData[2]),
Convert.ToByte(colorData[3]));
}
}
}
// generate script to clear any previous highlight shapes
sb.Append(VEShape.GenerateDeleteLayerScript(VEMap, vEHighlightLayer));
sb.Append(VEShape.GenerateAddLayerScript(VEMap, vEHighlightLayer));
// deserialize shape from response
Shape shp = Shape.DeserializeShape(row.ShapeSerial);
// which feature class we are highlighting?
// convert points and polylines to appropriate highlight polygon
if (shp is Polyline)
{
Polyline pLine = shp as Polyline;
shp = pLine.ToPolygon(10.0);
}
// now the resultant feature should be a polygon
if (shp is Polygon)
{
Polygon polyG = shp as Polygon;
// if the shape is valid and all rings and holes can be closed
if (polyG.Validate())
{
// reduce number of verticies to minimize traffic back to client
// only perform this operation on complex shapes
if (polyG[0].Count > 25)
{
// a reasonable tolerance to simplify (in decimal degrees)
// (this will likely need to be changed or calculated based on mapscale)
polyG.Simplify(.0001d);
}
// if simplification invalidates the poly, then use it's bounding box
if (!polyG.Validate())
{
polyG = polyG.Bounds.ToPolygon();
}
// generate polygon/s for VE - supports multiple rings
int sIdx = 0;
List<VEShape> shapes = VEShape.CreateFromPolygon(polyG);
foreach (VEShape shape in shapes)
{
// want the callout to appear where the mouse cursor is hovering
shape.IconAnchor = mapPoint84;
// only on the first shape, add a call-out
// multi-ring polygons only get one callout anyway
if (sIdx++ == 0)
{
shape.Title = "Info:";
shape.ForceShowInfoBox(VEMap);
// get back info on the first row
shape.Description = MapQueryManager1.GetListHTML().Replace('\"', '\'');
}
// set style
shape.FillColor = fillColor;
shape.LineColor = outlineColor;
shape.LineWidth = 2;
// addshape script to eval
sb.Append(shape.GenerateAddShapeScript(vEHighlightLayer));
}
}
}
else if (shp is ISC.MapDotNetServer.Common.Point)
{
// create a VE pushpin for a point
ISC.MapDotNetServer.Common.Point pt = shp as ISC.MapDotNetServer.Common.Point;
VEShape pushpin = VEShape.CreateFromPoint(pt);
pushpin.Title = "Info:";
pushpin.Description = MapQueryManager1.GetListHTML().Replace('\"', '\'');
sb.Append(pushpin.GenerateAddShapeScript(vEHighlightLayer));
}
}
}
else if (MapQueryManager1.QueryResponse != null &&
MapQueryManager1.QueryResponse.Error != null)
{
// Wrap any query errors as exceptions
throw new Exception(MapQueryManager1.QueryResponse.Error);
}
// Pass the script to the client to be processed
sb.Append("}catch(e){alert(e.description);}");
MapClientStateUpdate.EvalJavascript = sb.ToString();
}
}
}
catch (Exception ex)
{
// Let the client handle this error
MapClientStateUpdate.Error = ex.Message;
}
return MapClientStateUpdate;
}