Plotting Geography LineStrings on Google Maps and Virtual Earth
One problem of passing geography representations from SQL Server to web-mapping services such as Virtual Earth or Google Maps is that they treat the data as lying on a flat plane. This is fine for the geometry datatype, but presents a problem when trying to present geography data.
To demonstrate, consider the route taken by an aeroplane flying from Los Angeles (Latitude 34N, Longitude 118W) to London (Latitude 51.5N, Longitude 0), using the geography datatype.
SELECT geography::STLineFromText('LINESTRING(-118 34, 0 51.5)', 4326)
Visualising this LineString using the spatial results tab in SQL Server Management Studio gives the correct result that, when projected onto a Mercator projection map, appears as shown below:
The problem is, when creating the equivalent Polyline constructor in Google Maps or Virtual Earth from this WKT, the default behaviour is to create a straight line between the points (34,-118) and (51.5,0) along the flat map plane, in which case you end up with the following route:
var polyline = new VEShape(VEShapeType.Polyline, [new VELatLong(34,-118), new VELatLong(51.5,0)]); map.AddShape(polyline);
The solution
To get the map to correctly plot a linestring representing the great circle route between two points rather than simply a straight line on the map requires a different approach for Google Maps than for Virtual Earth.
Google Maps
The Google Maps Polyline constructor allows you to specify an option of geodesic: true, which will accurately portray the great circle route, as follows:
var polyline = new google.maps.Polyline( [new google.maps.LatLng(34,-118), new google.maps.LatLng(51.5,0)], "#ff0000", 3, 1, {geodesic:true}); map.addOverlay(polyline);
This results in the following map:
Virtual Earth
For Virtual Earth it's a little bit more complicated, because there is no inbuilt ability to draw geodesic shapes - you have to create your own function that calculates a number of waypoints along the length of the LineString, and then plots the segments between each waypoint to approximate the great cirlce route. That technique is demonstrated in the following code:function AddGeodesicPolyline(start, end) { var segments = 32; // The number of line segments used to approximate the true curved route var latLongs = new Array(); with (Math) { // Convert all coordinates to Radians var lat1 = start.Latitude * (PI / 180); var lon1 = start.Longitude * (PI / 180); var lat2 = end.Latitude * (PI / 180); var lon2 = end.Longitude * (PI / 180); // Calculate the total extent of the route var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2))); // Calculate the position at fixed intervals along the route for (var n = 0; n < segments + 1; n++) { var f = (1 / segments) * n; f = f.toFixed(6); var A = sin((1 - f) * d) / sin(d) var B = sin(f * d) / sin(d) // Calculate 3D Cartesian coordinates of the point var x = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2) var y = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2) var z = A * sin(lat1) + B * sin(lat2) // Convert these to latitude/longitude var lat = atan2(z, sqrt(pow(x, 2) + pow(y, 2))) var lon = atan2(y, x) // Create a VELatLong representing this location (remember to convert back to degrees) var p = new VELatLong(lat / (PI / 180), lon / (PI / 180)); // Add this to the array latLongs.push(p); } } // Create a new linestring from the LatLong array and add it to the map var linestring = new VEShape(VEShapeType.Polyline, latLongs); map.AddShape(linestring); }
This is shown in the following map:


Comments
Post new comment