|
|
8/26/2009 Currently Firefox 3.5 is not supported by Bing Maps although there are only minor issues. One issue is that if your page is scrollable and you use the mouse wheel to zoom in or out of the map the page will also scroll. This is not very good for the user experience. After some investigation I have put together the following workaround to correct this issue: | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script type="text/javascript"> var map = null; function GetMap() { map = new VEMap('myMap'); map.LoadMap(); //Detect if browser is Firefox 3.5 if (navigator.userAgent.indexOf("Firefox/3.5") != -1) { document.getElementById("myMap").addEventListener('DOMMouseScroll', WindowMouseWheelHandler, false); } } function WindowMouseWheelHandler(e) { e.stopPropagation(); e.preventDefault(); e.cancelBubble = false; return false; } </script> </head> <body onload="GetMap();" style="font-family:Arial"> <div id='myMap' style="position:relative; width:600px; height:400px;"></div> <!-- Page filling div to make the page scrollable--> <div style="height:10000px"></div> </body> </html> | 8/16/2009 fRecently I was asked how to determine if a specific area on the map has Birdseye imagery. There are several ways to do this using the AJAX control or the Web Service of Bing Maps. However, one of the clients requirements was to be able to determine this before loading a map and without having to create a web service. With this requirement it is not possible to use the built in imagery metadata functionality of the map. There is not supported method to determine if a location has Birdseye imagery without loading a map and without using the Bing Maps Web Services, however it can be done. In the series of blog posts I made called “Birdseye Imagery Extraction via the Virtual Earth Web Services” I described a method to retrieve Birdseye scene information from a background service that is used by the AJAX control. By extending some of the undocumented functionalities of the we can access this backend service through the AJAX control without loading a map. This backend service takes in an array of VEParameter objects. These parameters mush include the latitude, longitude, zoom level, spin direction, and orientation of a Birdseye request. By hard coding the zoom level to 19, the spin direction to “No Spin” and the orientation to “North” we have the highest chance of getting an accurate response as this is the most common Birdseye imagery setting. Once we have created this array we can pass it along with the URL of the backend service, and a callback function into a method called JSONRequestInvoke. In that callback the response contains a property called Scene. If this property is null then Birdseye imagery is not available for the specified location otherwise it does. Below is a basic HTML page that shows how to do this.
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Birdseye Imagery Query</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script> var BEURL = "http://dev.virtualearth.net/services/v1/ImageryMetadataService/ImageryMetadataService.asmx/GetBirdsEyeSceneByLocation"; function CheckBirdseye() { var lat = document.getElementById("lat").value; var lng = document.getElementById("lng").value; RequestBirdseyeMetadata(parseFloat(lat),parseFloat(lng)); } function RequestBirdseyeMetadata(lat,lng) { var params = []; params.push(new VEParameter("latitude",lat)); params.push(new VEParameter("longitude",lng)); params.push(new VEParameter("level",19)); params.push(new VEParameter("spinDirection","\"NoSpin\"")); params.push(new VEParameter("orientation","\"North\"")); JSONRequestInvoke(BEURL, params, BirdseyeMetadataCallback); } function BirdseyeMetadataCallback(response) { if(response.Scene != null) { alert("There is Birdseye Imagery at this location"); } else { alert("No Birdseye Imagery at this location"); } } </script> </head> <body> Latitude: <INPUT id="lat" type="text"><br/> Longitude: <INPUT id="lng" type="text"><br/> <INPUT type="button" value="Check for Birdseye Imagery" onclick="CheckBirdseye();"> </body> </html> | 6/15/2009 Currently the default behavior of the VEMap.Find function is to center the map over the matching location that is the most popular. This is great most of the time but sometimes it would be nice to be able to search for a location that is closest to where the map currently is. By default the top 10 most popular locations (VEPlace objects) are returned in the find callback. By modifying the properties in the find call up to 20 locations can be return. By setting another property you can prevent the Find method from centering over the most popular match automatically. By calculating the distance from the center of the map to each returned location in the Find callback we can determine the closest (distance) location that matches the users search. The distance from the center of the map to each point can be calculated using the haversine formula: http://rbrundritt.spaces.live.com/default.aspx?_c01_BlogPart=blogentry&_c=BlogPart&handle=cns!E7DBA9A4BFD458C5!317 The following code is an example of how to go about doing this. | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Find Nearest to Me</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script> var map = null; var earthRadius = 6367; //radius in km function GetMap() { map = new VEMap('myMap'); map.LoadMap(); } function getLatLong(layer, resultsArray, places, hasMore, veErrorMessage) { var origin = map.GetCenter(); if(places.length>0) { var location = places[0]; var distance = haversineDistance(origin,location.LatLong); for(var i=1;i<places.length;i++) { var d = haversineDistance(origin,places[i].LatLong); if(d < distance) { location = places[i]; distance = d; } } map.SetMapView([location.LatLong]); } } function FindLoc() { map.Find(null, document.getElementById('txtWhere').value, null, null, 0, 20, false, false, false, false, getLatLong); } function haversineDistance(latlong1,latlong2) { var lat1 = DegtoRad(latlong1.Latitude); var lon1 = DegtoRad(latlong1.Longitude); var lat2 = DegtoRad(latlong2.Latitude); var lon2 = DegtoRad(latlong2.Longitude); var dLat = lat2-lat1; var dLon = lon2-lon1; var cordLength = Math.pow(Math.sin(dLat/2),2)+Math.cos(lat1)*Math.cos(lat2)*Math.pow(Math.sin(dLon/2),2); var centralAngle = 2 * Math.atan2(Math.sqrt(cordLength), Math.sqrt(1- cordLength)); return earthRadius * centralAngle; } function DegtoRad(x) { return x*Math.PI/180; } </script> </head> <body onload="GetMap();"> <CENTER> <INPUT id="txtWhere" type="text" name="txtWhere" value=> <INPUT id="find" type="button" value="Find" onclick="FindLoc();"><br/> <div id='myMap' style="position:relative; width:600px; height:400px;"></div> </CENTER> </body> </html> | 5/31/2009 Sometimes adding an arrow head to polyline is desired. There are a couple of ways to do this. One way is to use arrow head images as icons and rotate to point in the desired direction. A second method is to extend the polyline and draw the arrow point. The flowing code shows you how to implement the second method.
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script> var earthRadius = 6367; //radius in km var map; var point1 = new VELatLong(40.02,-105.03); var point2 = new VELatLong(40.02,-105.02); var point3 = new VELatLong(40.015,-105.025); function GetMap() { map = new VEMap('mymap'); map.LoadMap(); var points = generatePolylinePointsWithArrow([point1,point2,point3]); var polyline = new VEShape(VEShapeType.Polyline,points); polyline.HideIcon(); map.AddShape(polyline); map.SetMapView(points); } function generatePolylinePointsWithArrow(points) { //last point in polyline array var anchorPoint = points[points.length-1]; //bearing from last point to second last point in pointline array var bearing = calculateBearing(anchorPoint,points[points.length-2]); //length of arrow head lines in km var arrowLength = 0.05; //angle of arrow lines relative to polyline in degrees var arrowAngle = 15; //calculate coordinates of arrow tips var arrowPoint1 = calculateCoord(anchorPoint, bearing-arrowAngle, arrowLength); var arrowPoint2 = calculateCoord(anchorPoint, bearing+arrowAngle, arrowLength); //go from last point in polyline to one arrow tip, then back to the //last point then to the second arrow tip. points.push(arrowPoint1); points.push(anchorPoint); points.push(arrowPoint2); return points; } function DegtoRad(x) { return x*Math.PI/180; } function RadtoDeg(x) { return x*180/Math.PI; } function calculateCoord(origin, brng, arcLength) { var lat1 = DegtoRad(origin.Latitude); var lon1 = DegtoRad(origin.Longitude); var centralAngle = arcLength /earthRadius; var lat2 = Math.asin( Math.sin(lat1)*Math.cos(centralAngle) + Math.cos(lat1)*Math.sin(centralAngle)*Math.cos(DegtoRad(brng))); var lon2 = lon1+Math.atan2(Math.sin(DegToRad(brng))*Math.sin(centralAngle)*Math.cos(lat1),Math.cos(centralAngle)-Math.sin(lat1)*Math.sin(lat2)); return new VELatLong(RadtoDeg(lat2),RadtoDeg(lon2)); } function calculateBearing(A,B) { var lat1 = DegtoRad(A.Latitude); var lon1 = A.Longitude; var lat2 = DegtoRad(B.Latitude); var lon2 = B.Longitude; var dLon = DegtoRad(lon2-lon1); var y = Math.sin(dLon) * Math.cos(lat2); var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon); var brng = (RadtoDeg(Math.atan2(y, x))+360)%360; return brng; } </script> </head> <body onload="GetMap();"> <div id='mymap' style="position:relative; height:400px; width:800px;"></div> </body> </html> | 5/18/2009 Virtual Earth has a lot of options for for working with shapes, unfortunately there are so many additional options that are not document (thus unsupported) that are available. However, just because it's not documented doesn't mean it can't be used, it just means that it may break at any time and if it does then it's no ones fault but your own. Now that the fair warning is out of the way here is how to draw dashed polylines. Before adding a polyline to the map set the VEShapeStyle.prototype.stroke_dashstyle property to one of the following: - Solid
- DashDot
- ShortDash
- ShortDot
- ShortDashDot
- ShortDashDotDot
- Dot
- Dash
- LongDash
- LongDashDot
- LongDashDotDot
For an added effect, resize the width of the polyline based on the zoom level. I've put together a nice little sample of how to do this which can be downloaded here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/dashedLines.zip Here is a screen shot of what the sample program looks like:  Out of the box Virtual Earth has the ability to draw multipoint routes however it does not have the ability to display more than one route object at a time. Questions on how to do this have been appearing on the Virtual Earth forums for a while now. The general method to do this is to use client tokens with your map so that you can retrieve the route geometry from the Virtual Earth. Once you have the route geometry you can then use polylines to draw any number of routes on your map with out the previous map disappearing. To make things easy I create my own Route object. This object has a polyline which is used to draw the route path, an array of icons to mark the turn by turn points, a directions property which contains HTML that displays the turn by turn directions, a route title which is used to label the route in a list next to the map so that we can display the route markers and directions for a different route, a route color in both Hex and RGB format. RGB format is needed for VE, and hex format is needed for HTML. This object has a prototype called ShowRouteInfo, which hides all pushpins on the map, displays the directions for the route, shows the pushpins icons for the route, and sets the best map view for the route. This object looks like s: | //create a common route object to store only the information we need function Route() { this.polyline = null; this.icons = []; this.directions = ""; this.routeTitle = ""; this.routeColorHex = ""; this.routeColorRGB = new VEColor(0,0,255,1); } Route.prototype.ShowRouteInfo = function() { //hide all pins on pin layer HideAllPins(); //add directions to directions panel document.getElementById('directions').innerHTML = this.directions; //show route icons for (var i = 0, j = this.icons.length; i < j; i++) { this.icons[i].Show(); } //set the map view to show the route map.SetMapView(this.polyline.GetPoints()); } | One catch with the routing information that is returned by VE is that if you try and get the custom icon for a leg of the route using the GetcustomIcon method on the VERouteLeg.Shape property you will end up with the default red icon. We can however access the url to the correct icon by referencing the _customIcon property of the VERouteLeg.Shape object. We will also want to create a new pushpin object otherwise our pushpin will not exist after we delete the default route. A simple function to create the correct route icon looks like this: | //copy a route pin so that we can delete the route from the map function CopyRoutePin(shape) { var newShape = new VEShape(shape.GetType(), shape.GetPoints()); newShape.Title = shape.GetTitle(); newShape.Description = shape.GetDescription(); //using the GetCustomIcon method on a route pin gets the standard red pin //The _customIcon property of the route icon is the url to the correct icon var customIcon = new VECustomIconSpecification(); customIcon.TextContent = " "; customIcon.Image = shape._customIcon; newShape.SetCustomIcon(customIcon); return newShape; } | The rest of the work done in the code base for this is pretty straight forward. The complete source code can be downloaded here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/MultipleVERoutes.zip Here is a screen shot of this code in action:  4/16/2009 Virtual Earth is a great tool for viewing geo-spatial information on a map. It offers great user experience by being able to pan and zoom the map. Sometimes you may wish you could do this with a static image of your own. In Silverlight you can use DeepZoom to do this but doing this in a regular HTML web page requires a lot of custom JavaScript. So why not use Virtual Earth to do this for you? There are a couple of ways to do this. One is to us MapCruncher and generate a bunch of tile images and then add them to the map as a tile layer. This often results in a large number of tiles. This will work in both 2D and 3D modes which is great if your viewing geo-spatial imagery but if you only need 2D tools there are other options.
Another option is to add the image to the map like a pushpin and to resize it depending on the zoom level. Recently Chris Pendleton blogged about an interesting way to add pushpins to the map by creating image tags and adding them to a div and then adding the div to the map as a shape layer. You can view the post here: http://blogs.msdn.com/virtualearth/archive/2009/04/09/virtual-earth-api-release-information-april-2009.aspx
By expanding upon the methods described in this post we can view an image the same way we view a Virtual Earth map. Here is some sample code that demonstrates how to do this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script type="text/javascript" src=" http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script type="text/javascript">
var map = null; var myLayer = null; var pinImage = 'http://www.okanagan.com/ski/sun_peaks_map.jpg';
//image dimensions used form scaling var imageHeight = 904; var imageWidth = 1025;
var previousZoomLevel = null;
//RedrawShapes function RedrawShapes() { var pixel = map.LatLongToPixel(new VELatLong(85,-180)); var zoom = map.GetZoomLevel(); var mapWidth = 256 * Math.pow(2,zoom); var mapHeight = mapWidth * imageHeight/imageWidth; myLayer.innerHTML = "<img src='" + pinImage + "' style='position:absolute; left:" + pixel.x + "px; top:" + pixel.y + "px;width:"+mapWidth+"px;height:"+mapHeight+"px;' +/>"; }
//View Change Event handler function EventViewChange() { //if zoom level changed, then redraw the shapes, otherwise do nothing //-- the map will position the pins on pan properly var currentZoomLevel = map.GetZoomLevel(); if (previousZoomLevel != currentZoomLevel) { previousZoomLevel = currentZoomLevel; ClearShapes(); RedrawShapes(); } }
//Event to respond to Birdseye changes function EventBirdseyeChanged() { //When Birdseye rotation changes, redraw the shapes ClearShapes(); RedrawShapes(); }
//Clear the shapes function ClearShapes() { myLayer.innerHTML = ''; }
//Start Zoom event handler function EventStartZoom() { //When zoom is staretd clear the shapes before zoom since they will need to be redrawn ClearShapes(); }
//Load Map Event Handler function EventMapLoaded() { //Store previous zoom level as current zoom level previousZoomLevel = map.GetZoomLevel();
CreateLayer();
//Register for events to update the custom layer map.AttachEvent("onchangeview", EventViewChange); map.AttachEvent("onstartzoom", EventStartZoom); map.AttachEvent("onobliquechange", EventBirdseyeChanged); RedrawShapes(); } function CreateLayer() { //Create the custom layer for the map dynamically on the map surface myLayer = document.createElement('div'); myLayer.style.position = "absolute"; myLayer.style.top = "0px"; myLayer.style.left = "0px"; myLayer.style.width = "500px"; myLayer.style.height = "400px"; myLayer.style.zIndex = 1000; map.AddCustomLayer(myLayer); }
function CreateMap() { var mapOptions = new VEMapOptions(); mapOptions.LoadBaseTiles = false; map = new VEMap('myMap'); map.onLoadMap = EventMapLoaded; map.LoadMap(null,1,null,null,null,null,null,mapOptions); map.HideDashboard(); map.HideScalebar(); } </script> </head> <body onload="CreateMap();"> <div id='myMap' style="position:relative; width:500px; height:400px;"></div> </body> </html>
4/10/2009 It was recently brought to my attention that routes are not supported in the Bird's eye map view. Some how this slipped by me as I swear I've seen it working before. After some thought I suspect that this has to do with possible accuracy issues. A few releases ago a new map method was introduced called VEMap.SetShapeAccuracy (http://msdn.microsoft.com/en-us/library/bb877873.aspx). This method only increases the accuracy of pushpin's. Currently if you draw a route and try an view it in the bird's eye map view you will find that there is no route line drawn only segment markers. One way to correct this issue is to retrieve the route geometry (requires a client token) and draw a polyline in place of the route line. Using this method will not increase the accuracy but will allow you to see a route line. In my tests I haven't noticed any real accuracy issues using this method. Complete source code that demonstrates this method can be found here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/BirdseyeRouting.zip
Here is a screen shot of a bird's map with a route drawn on it using this method:

3/12/2009 When using the AJAX version of the Virtual Earth control people have had issues making multiple find calls, one after the other. The resulting information does not always return in the order expected. The way around this issue is to use recursion. This will essentially make one call at a time and wait for one find call to finish before making another. The following code demonstrates how to do this:
|
<html> <head> <title></title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <script src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script> var map = null; var count = 0; var locations = ["Toronto, Ontario","New York, New York","Seattle, WA"]; function GetMap() { map = new VEMap('myMap'); map.LoadMap(); }
function FindLocation(searchstr) { if (searchstr != '') { map.Find(null, searchstr, null, null, null, null, false, null, null, false, AddPin); } } function AddPin(layer, resultsArray, places, hasMore, veErrorMessage) { if(places.length > 0) { var shape = new VEShape( VEShapeType.Pushpin,places[0].LatLong); shape.SetTitle(places[0].Name); map.AddShape(shape); } count++; if(count<locations.length) FindLocation(locations[count]); } </script> </head> <body onload="GetMap();"> <div id='myMap' style="position:relative; width:800px; height:600px;"></div> <INPUT type="button" value="Find Locations" onclick="FindLocation(locations[0]);"> </body> </html> | 2/17/2009 Virtual Earth currently supports polygons that consist of a single array of points. Many geospatial systems have complex polygons which may consist of several arrays of points which are a combination of outer and inner arrays of points. These are often referred to as Multipolygons. Multipolygons are often used to represent complex boundary areas such as "donut" shaped polygons. By properly concatenating inner and outer polygon array segments in the proper order a single array can be created to represent a complex polygon. An array of polylines can then be created to represent the edges of the polygon. The following method takes in an array of inner and outer polygon array segments and returns and an array of shapes. This array consists of a polygon and and several polylines. This array of shapes can then be added to the map either by looping through each shape and adding it to the map, or by passing the array through the MultiShape class described here: http://rbrundritt.spaces.live.com/blog/cns!E7DBA9A4BFD458C5!605.entry The MultiShape class will give you greater control of the complex polygon. | function CreateAdvancePolygon(polyPoints) { if(polyPoints.length > 0) { var anchor = polyPoints[0][0]; var points = polyPoints[0].concat(anchor); var lines = new Array(); var line = new VEShape(VEShapeType.Polyline,points); line.HideIcon(); lines.push(line); for(var i=1;i<polyPoints.length;i++) { points = points.concat(polyPoints[i],polyPoints[i][0],anchor); var line = new VEShape(VEShapeType.Polyline,polyPoints[i].concat(polyPoints[i][0])); line.HideIcon(); lines.push(line); } var polygon = new VEShape(VEShapeType.Polygon, points); polygon.SetLineColor(new VEColor(0,0,0,0)); polygon.HideIcon(); return lines.concat(polygon); } return null; } | Below is an example of a complex polygon that was created using the above method. Complete sample code for this polygon can be found here: http://cid-e7dba9a4bfd458c5.skydrive.live.com/self.aspx/VE%20Sample%20code/AdvancePolygon.zip
1/18/2009 In the latest release of Virtual Earth, version 6.2, client side clustering was included as a built in function for shape layers. You can assign a client side clustering algorithm to a shape layer so that it will automatically cluster the shapes when the map is zoomed. This is done by using the VEShapeLayer.SetClusteringConfiguration method. This method can take in two parameters. A clustering type and a VEClusteringOptions object. Currently there is only one built in clustering algorithm type available, grid clustering. Unfortunately there is limited flexibility in this algorithm in that the grid size can not be modified. This can be resolved by making a custom clustering algorithms. This post will show how to create a custom clustering algorithm that can be used with the VEShapeLayer.SetClusteringConfiguration method. According to the documentation (http://msdn.microsoft.com/en-us/library/cc966716.aspx) a custom clustering algorithm must take in a VEShapeLayer and return an array of VEClusterSpecification objects. A VEClusterSpecification objects has two properties. A Shapes property and a LatLong property. The Shapes object consists of an array shapes that are being clustered. The LatLong property is the coordinate used to place the clustered pushpin. (http://msdn.microsoft.com/en-us/library/cc966730.aspx) The custom clustering algorithm that will be created in this blog post will implement a grid clustering algorithm and place the clustered pushpins in the mean average coordinate of the clustered pins. This algorithm is an improvement upon the clustering algorithm described here: http://msdn.microsoft.com/en-us/library/cc161072.aspx Here is the custom clustering algorithm which grid clusters and places the clusters in the mean average coordinate of the clustered pins. You can modify the size of the grid used in the algorithm by changing the value of the gridSize variable. function meanAverageGridCluster(baseLayer) { var cluster = new Array(); //the size of the grid in pixels var gridSize = 30; //Calculate the size of the map in pixels var mapView = map.GetMapView(); var bottomRight = map.LatLongToPixel(mapView.BottomRightLatLong); var mapWidth = parseInt(Math.ceil(bottomRight.x)); var mapHeight = parseInt(Math.ceil(bottomRight.y)); //break the map into a grid var numXCells = parseInt(Math.ceil(mapWidth / gridSize)); var numYCells = parseInt(Math.ceil(mapHeight / gridSize)); //create an array to store all the grid data. var gridCells = new Array(numXCells*numYCells); //Itirate through the shapes in the base layer for(var cnt = 0; cnt < baseLayer.GetShapeCount(); cnt++) { //convert the shapes latlong to a pixel location var shape = baseLayer.GetShapeByIndex(cnt); var latLong = (shape.GetPoints())[0]; var pixel = map.LatLongToPixel(latLong); var xPixel = pixel.x; var yPixel = pixel.y; //check to see if the shape is within the bounds of the viewable map if(mapWidth >= xPixel && mapHeight >= yPixel && xPixel >= 0 && yPixel >= 0) { //calculate the grid position on the map of where the shape is located var i = Math.floor(xPixel/gridSize); var j = Math.floor(yPixel/gridSize); //calculates the grid location in the array var key = i+j*numXCells; if(gridCells[key]==null) { gridCells[key] = new VEClusterSpecification(); gridCells[key].Shapes = new Array(); gridCells[key].Shapes.push(shape); } else { gridCells[key].Shapes.push(shape); } } } //Iterate through the clustered data in the grid array for(var key = 0; key < gridCells.length; key++) { //calculate mean clustered coordinate if(gridCells[key] != null) { var size = gridCells[key].Shapes.length; var latSum = 0; var lonSum = 0; for(var i=0;i<size;i++) { var point = gridCells[key].Shapes[i].GetPoints()[0]; latSum += point.Latitude; lonSum += point.Longitude; } gridCells[key].LatLong = new VELatLong(latSum/size,lonSum/size); cluster.push(gridCells[key]); } } return cluster; } The following code can be used to implement this customer clustering algorithm. var map = null; function GetMap() { map = new VEMap('myMap'); map.LoadMap(); var clusterLayer = new VEShapeLayer(); clusterLayer.SetClusteringConfiguration(meanAverageGridCluster); map.AddShapeLayer(clusterLayer); //Import data from source. In this case a GeoRSS is used. var veLayerSpec = new VEShapeSourceSpecification(VEDataType.GeoRSS, "georsstest.xml", clusterLayer); map.ImportShapeLayerData(veLayerSpec); }
10/2/2008 Virtual Earth gives us the ability to get turn by turn directions which is great. However the directions may take longer to read through when you need to know which way to turn at the last minute. One way to aid this is to bold the key words in the directions so that they stand out. The following code is an simple example of how to display the plain text, un-bolded directions.
function OnGotRoute( route ) { var legs = route.RouteLegs; var leg = null; var directions = ""; for (var i=0; i < legs.length; i++) { leg = legs[i]; legItinerary = leg.Itinerary; for ( var j=0; j< legItinerary.Items.length; j++ ) { item = legItinerary.Items[j]; directions += item.Text+"<br/>"; } } document.getElementById("directions").innerHTML = directions; }
We can determine which words to bold by using regular expressions. Searching and bolding every keyword would require a list of all the possible key words which would result in a really long regular expression. However, there are a limited number of words that are commonly used by Virtual Earth in the directions which we don't want to bold. Knowing this we can bold the key words by bolding the complete directions instruction and un-bolding all the common words. The following code example is a modified version of the previous code that bolds the complete direction instruction and un-bolds all common words, thus bolding all keywords. function OnGotRoute( route ) { var legs = route.RouteLegs; var leg = null; var directions = ""; for (var i=0; i < legs.length; i++) { leg = legs[i]; legItinerary = leg.Itinerary; for ( var j=0; j< legItinerary.Items.length; j++ ) { item = legItinerary.Items[j]; directions += "<b>"+item.Text.replace(/\b(Take| Turn|^Turn|Bear|Depart|onto|for|and|the|then|Keep |follow|signs|toward|At|exit|ramp|stay|on|to|Make a|Road name changes|Arrive)\b/gi,"</b>$1<b>")+"</b><br/>"; } } document.getElementById("directions").innerHTML = directions; }
The following is an example of set of bolded directions from zip code 12345 to zip code 04556:
Depart River Rd Turn left onto Edison Ave Turn right onto SR-5 / State St Turn right onto Broadway Take ramp left and follow signs for I-890 / Schenectady County Memorial Veteran Hwy East Take ramp for I-90 East / Gov Thomas E Dewey Thruway East / New York State Thruway East toward Albany Keep right onto I-87 South / Gov Thomas E Dewey Thruway South / New York State Thruway South At exit 21A, take ramp right for Gov Thomas E Dewey Thruway East / New York State Thruway East toward Mass Tpke / Boston Keep straight onto I-90 East / Gov Thomas E Dewey Thruway East / New York State Thruway East At exit 11A, take ramp right for I-495 North toward Marlborough / N.H.-Maine Keep straight onto I-95 North At exit 44, take ramp right for I-295 North toward South Portland / Downtown Portland At exit 28, take ramp right toward Coastal Route / Brunswick / Bath Keep straight onto US-1 North / Blue Star Memorial Hwy Keep left to stay on US-1 / Blue Star Memorial Hwy / Mill St Keep left to stay on US-1 Make a U-turn at Cochran Rd Turn left onto SR-27 Turn left onto Old County Rd Road name changes to E Old County Rd Turn left onto road Arrive on the left
|