| Johannes's profileHannes's Virtual Earth B...BlogListsSkyDrive | Help |
Hannes's Virtual Earth Blog |
|||||
|
November 13 Bing Maps at TechEd EuropeOn behalf of our friends from Borchert GeoInfo and the Bing Maps team I would like to thank everybody who visited us at the booth during the TechEd in Berlin or attended one of my sessions. Some of you asked for my presentations and the sample code. You will find everything here on my SkyDrive: Have a look at the Readme.pdf if you are unsure about what you need. November 08 Bing Maps at TechEd EuropeTomorrow the TechEd Europe opens it’s gates in Berlin and Bing Maps will be represented as well. Chris Pendleton will come over from Redmond and join us for the week and I have the privilege of presenting 2 sessions on Bing Maps:
Here is a quick Bing Maps Collection with some MapCruncher layers that shows the venue and helps you find the Bing Maps booth and the locations for my sessions :-) You can also jump straight into a 3D-tour. I’m looking forward to see some of you in Berlin. September 02 Bing Maps & SQL Server 2008 R2 Reporting ServicesSQL Server 2008 R2 is the next generation of the Microsoft SQL Server platform. The release is planned for the first half of calendar year 2010 but for those who can’t wait there is as always a community technology preview (CTP). The August CTP has lots of new features and from the mapping perspective the most interesting one is the integration of a maps in SQL Server Reporting Services. With just a few mouse-clicks you can generate thematic maps from spatial data stored as GEOMTRY or GEOGRAPHY data types in SQL Server 2008 or from ESRI SHP-files and you can use Bing Maps roads, aerial or hybrid images as background data. If you are as nosy as me, you can download the CTP here. Check it out it’s really incredible simple.
August 21 Bing Maps & WikipediaIf you have been using the “Explore Collections” feature in the consumer facing implementation of Bing Maps before you may have wondered if it is possible to get this feature into your own Bing Maps implementation as well. Indeed that is possible and there is a quite simple approach. In the following walkthrough we will get specifically Wikipedia content into our Bing Maps. Let’s start with a closer look how the consumer side does it: If we go to Bing Maps and search for a location like “Tower of London” we’ll find that we can explore collections for this location. These collections are basically a whole lot of community content that was created in Bing Maps collections, is available as GeoRSS, KML, KMZ or GPX on the internet and was found by the crawlers or is integrated from Wikipedia and Photosynth. We can filter this content, apply different sort criteria such as distance and then we could subscribe to an RSS-feed with the results. A closer look at the RSS-feed will show that it is in fact a GeoRSS-feed and we know of course that we can import GeoRSS-feeds into Bing Maps using the VEMap.ImportShapeLayerData-method. What we really want is however not a static feed, we want to update the results when we pan or zoom the map so let’s have a closer look at the URL of the feed: So in fact we are calling a web service that generates the GeoRSS-feed dynamically and the parameter bbox contains the bounding box with the South-West and North-East corner of the area for which we want to retrieve the data. Well that is simple enough to implement but there is one more thing to consider: If we call a GeoRSS-feed that is in a different domain we get an annoying security warning from our browser: To avoid this security warning we can set up a proxy as described by Mike McDougall here. Our HTML- and JavaScript code could look like 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://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2"></script> <script type="text/javascript"> var map = null; //VEShapeLayer var slGeoRSS = new VEShapeLayer(); function GetMap() { map = new VEMap('myMap'); map.LoadMap(new VELatLong(51.508145,-0.07626), 17, 'h', false); } function AddShape(control) { if (document.getElementById(control).checked == false) { //Delete all Shapes slGeoRSS.DeleteAllShapes(); //Detach Map-Events map.DetachEvent("onendpan", LoadData); map.DetachEvent("onendzoom", LoadData); } else { //Attach Map-Events map.AttachEvent("onendpan", LoadData); map.AttachEvent("onendzoom", LoadData); LoadData(); } } function LoadData() { map.DeleteAllShapes(); //Retrieve the boundaries of the mapview var nePixel = new VEPixel(600, 0); //North-East corner of the map view var swPixel = new VEPixel(0, 400); //South West corner of the map view var neLatLon = map.PixelToLatLong(nePixel); var neLat = neLatLon.Latitude; var neLon = neLatLon.Longitude; var swLatLon = map.PixelToLatLong(swPixel); var swLat = swLatLon.Latitude; var swLon = swLatLon.Longitude; //Build URL to call the server var url = "./GeoRSS-Proxy.ashx?source=http://www.bing.com/maps/GeoCommunity.asjx?"; url += "action=retrieverss&mkt=en-gb&ss=&bbox="; url += swLon + ","; url += swLat + ","; url += neLon + ","; url += neLat; url += "&startindex=0&order=distance&tag=Wikipedia"; var veLayerSpec = new VEShapeSourceSpecification(VEDataType.GeoRSS, url, slGeoRSS); map.ImportShapeLayerData(veLayerSpec, onGeoRSSLoad, false); } function onGeoRSSLoad(a, b) { var numShapes = slGeoRSS.GetShapeCount(); var numPoints = 0; for (var i = 0; i < numShapes; ++i) { var s = slGeoRSS.GetShapeByIndex(i); s.SetCustomIcon("IMG/wikipedia.gif"); } } </script> </head> <body onload="GetMap();"> <div id='myMap' style="position:absolute; top:0px; left:0px; width:600px; height:400px;"></div><br /> <div id='divCtrl' style="position:absolute; top:400px; left:0px; width:600px;" > <input id="cbGeoRSS" type="checkbox" onclick="AddShape('cbGeoRSS')" /><a>Wikipedia</a><br /> </div> </body> </html> And here is the proxy implemented as a Generic WebHandler <%@ WebHandler Language="VB" Class="GeoRSS_Proxy" %> Imports System Imports System.Web Imports System.Net Imports System.IO Public Class GeoRSS_Proxy : Implements IHttpHandler Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest Dim myUrl As String = "" myUrl = context.Request.QueryString(0) For i = 1 To context.Request.QueryString.Count - 1 myUrl = myUrl + "&" + context.Request.QueryString.AllKeys(i) + "=" + context.Request.QueryString(i) Next 'Dim source As String = context.Request.QueryString("source") context.Response.ContentType = "text/xml" context.Response.ContentEncoding = System.Text.Encoding.UTF8 Dim request As HttpWebRequest = DirectCast(HttpWebRequest.Create(myUrl), HttpWebRequest) Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse) Dim stream As StreamReader = New StreamReader(response.GetResponseStream(), Encoding.ASCII) context.Response.Write(stream.ReadToEnd()) End Sub Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class The sample code is available here:
July 01 Route Optimization in Bing Maps powered by OnTerra’s free Stop Optimization ServiceOnTerra is a Microsoft Partner specialized on tailored Bing Maps solutions and focussing on but not limited to tracking and fleet management. Recently they launched a free beta version of a Stop Optimization Service. The service allows you to send an unlimited list of stops for your route and receive a string with the order of the stops optimized for the shortest driving distance. Let’s have a quick look at how it works: Bing Maps supports out-of-the-box Multi-Waypoint Routing for up to 25 stops through the method VEMap.GetDirections. However, the routing algorithm processes the stops always in the order in which they appear in the array of locations. If we want to start for example a trip in the Microsoft Office in Reading and want to visit Swindon, Oxford, Maidenhead and Newbury before we return to the Microsoft office we have to know in which order we want to visit these cities. If we just send the list in the order mentioned above it will guide us from one location to the next in exactly this order and come up with a route that is 185 miles long and takes about 3 hours and 20 minutes of drive time. The free OnTerra Stop Optimization Service figures out in which order we should drive for the shortest distance. In the example above it will suggest that we go to Maidenhead first, then Oxford, Swindon and Newbury. This will save us 45 miles and about 40 minutes of drive time. That’s not bad at all but if you would like to use it for example as a dispatcher in a fleet management application you also need to consider the times when you can make a pickup or a delivery, you may want to optimize for shortest time rather than shortest distance or you may need to consider height and weight restrictions that apply to your trucks. This is not part of the free service but in addition to the free stop optimization, OnTerra also offers such advanced features for a fee. If you are interested in this type of advanced service contact routeopt@onterrasys.com for more details. To use the free stop optimization service you will need to register and apply for a token. It requires 3 parameters:
You see that we need to geocode the locations before we send them to the stop optimization service. In the sample application above I use the Bing Maps AJAX control and use the callback function for the VEMap.Find-method to concatenate a string with the locations as expected by the stop optimization service, e.g. “txtStop1,51.461179,-0.925943#txtStop2,51.561765,-1.781815#txtStop3,51.522375,-0.727256#txtStop4,51.405876,-1.325891#txtStop5,51.756205,-1.259490”. Now here is one thing so consider: The optimization service splits the locations-string whenever it finds the character ‘#’. Unfortunately there appears to be a bug(?) which doesn’t process the string correctly when you work with the full number of decimal digits that comes back from the Bing Maps geocoder. In order to work around this bug(?) we can truncate the number of decimal digits to 6. This does actually not have a noticeable impact on the precision of the result but solves our problem. Once we have our last location we call a JavaScript-function StopOpt which actually creates an AJAX-call //Build String for Route Optimization function AppendLocations(layer, resultsArray, places, hasMore, veErrorMessage) { i = i + 1; if (locations.length > 0) { locations=locations+"#" } locations = locations + "txtStop" + i + "," The AJAX-call goes to a web handler which will call the stop optimization service and hands over the locations-string as well as a parameter that indicates if we’re doing a roundtrip or a one-way-trip. The optimized order of the result is received as a string and we process it a bit before we call the VEMap.GetDirections method. function StopOpt() { //Build URL to call the server var url = "./06-StopOpt.ashx?"; url += "locations=" + locations; if (document.getElementById("cbRoundtrip").checked == true) { url += "&roundtrip=true" } else { url += "&roundtrip=false" } //Get the appropriate XMLHTTP object for the browser var xmlhttp = GetXmlHttp(); //if we have a valid XMLHTTP object if (xmlhttp) { xmlhttp.open("GET", url, true); // varAsynx = true //set the callback xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) //4 is a success { //web service returns the optimized order of the stops var result = xmlhttp.responseText var stopArray = result.split(" >> "); var stops = new Array(); var order = "Order (Optimized):<br>"; for (var i = 0; i < stopArray.length; ++i) { order = order + document.getElementById(stopArray[i]).value + "<br>"; stops.push(document.getElementById(stopArray[i]).value); } if (document.getElementById("cbRoundtrip").checked == true) { order = order + document.getElementById("txtStop1").value; stops.push(document.getElementById("txtStop1").value) } var options = new VERouteOptions; options.RouteCallback = DistTime; document.getElementById("pOrder").innerHTML = order; map.GetDirections(stops, options); } } xmlhttp.send(null); } } Finally, here is our web handler that we have been calling with our AJAX-call and which in turn calls the OnTerra stop optimization service: Imports System.Web Imports System.Web.Services Imports BM_Azure_01_WebRole.OnTerra Public Class _06_StopOpt Implements System.Web.IHttpHandler Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest 'Get the URL-Parameters Dim locations As String = context.Request.Params("locations") Dim roundTrip As Boolean = CBool(context.Request.Params("roundtrip")) Dim token As String = "YOUR TOKEN" Dim svcOT As New OnTerra.OnTerraStopOptClient("basicEndPoint") Dim output As String output = svcOT.GetStopOpt(locations, roundTrip, token) context.Response.ContentType = "text/plain" context.Response.Write(output) End Sub ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable Get Return False End Get End Property End Class
|
|
||||
|
|