ArcGIS API for Flex does not support curved geometries directly, so instead we must approximate the shape of a geodesic curve by creating a polyline containing several small segments. Using a larger number of segments will make the polyline appear more smooth and more closely resemble the shape of the smooth curve, but will also increase its complexity. Using 32 segments is more than sufficient accuracy for most maps. We???ll call this value n.var n = 32;
Then, we need to determine the overall extent of the route, which we???ll call d. The shortest distance between any two points on a sphere is the great circle distance. Assuming that the coordinates of the start and end points are (lat1, lon1) and (lat2, lon2) respectively, measured in Radians, then we can work out the great circle distance between them using the Haversine formula, as follows:var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
We then determine the coordinates of the endpoints of each segment along the geodesic path. If f is a value from 0 to 1, which represents the percentage of the route travelled from the start point (lat1,lon1) to the end point (lat2,lon2), then the latitude and longitude coordinates of the point that lies at f proportion of the route can be calculated as follows: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);
By repeating the above with different values of f, (the number of repetitions set according to the number of segments in the line), we can construct an array of latitude and longitude coordinates at set intervals along the geodesic curve from which a polyline can be constructed.Complete function:
private function ToGeodesic(points:Array, n:int):Array {
if (!n) { n = 32 }; // The number of line segments to use
var locs:Array = new Array();
for (var i:int = 0; i < points.length - 1; i++) {
with (Math) {
// Convert coordinates from degrees to Radians
var lat1:Number = points.y * (PI / 180);
var lon1:Number = points.x * (PI / 180);
var lat2:Number = points[i + 1].y * (PI / 180);
var lon2:Number = points[i + 1].x * (PI / 180);
// Calculate the total extent of the route
var d:Number = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
// Calculate positions at fixed intervals along the route
for (var k:int = 0; k <= n; k++) {
var f:Number = (k / n);
var A:Number = sin((1 - f) * d) / sin(d);
var B:Number = sin(f * d) / sin(d);
// Obtain 3D Cartesian coordinates of each point
var x:Number = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
var y:Number = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
var z:Number = A * sin(lat1) + B * sin(lat2);
// Convert these to latitude/longitude
var lat:Number = atan2(z, sqrt(pow(x, 2) + pow(y, 2)));
var lon:Number= atan2(y, x);
// Create a Location (remember to convert back to degrees)
var p:MapPoint = new MapPoint(lon / (PI / 180),lat / (PI / 180));
// Add this to the array
locs.push(p);
}
}
}
return locs;
}
Sample Application:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:esri="http://www.esri.com/2008/ags"
pageTitle="Geodetic Curve"
applicationComplete="application1_applicationCompleteHandler(event)">
<!--
This sample shows how to create geodetic line.
ArcGIS Flex API does not support curved geometries directly, so instead we must approximate the shape of a geodesic curve
by creating a polyline containing several small segments.
Using a larger number of segments will make the polyline appear more smooth and more closely resemble the shape of the smooth
curve, but will also increase its complexity.
I find that using 32 segments is more than sufficient accuracy for most maps.
-->
<fx:Script>
<![CDATA[
import com.esri.ags.Graphic;
import com.esri.ags.SpatialReference;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.geometry.Multipoint;
import com.esri.ags.geometry.Polyline;
import com.esri.ags.symbols.SimpleLineSymbol;
import mx.events.FlexEvent;
private function addLine():void
{
//create a straight line between two points
var myPolyline:Polyline = new Polyline(
[[
new MapPoint(19.04,51.16),
new MapPoint(-93.98,45.44)
]], new SpatialReference(4326));
var myGraphicLine:Graphic = new Graphic(myPolyline);
myGraphicLine.symbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_DASH, 0xDD2222, 1.0, 4);
myGraphicsLayer.add(myGraphicLine);
//Point Array
var pointarray:Array=new Array(new MapPoint(19.04,51.16),new MapPoint(-93.98,45.44));
//Create Geodetic line
//Following function takes two input: PointArray and number of Segments and returns and Array
var Geoarr:Array = ToGeodesic(pointarray,32);
var geoPolyline:Polyline=new Polyline();
geoPolyline.addPath(Geoarr);
var GeoLine:Graphic = new Graphic(geoPolyline);
GeoLine.symbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, 0xDD2222, 1.0, 4);
myGraphicsLayer.add(GeoLine);
}
// Creates geodesic approximation of the lines drawn between an array
// of points, by dividing each line into a number of segments.
private function ToGeodesic(points:Array, n:int):Array {
if (!n) { n = 32 }; // The number of line segments to use
var locs:Array = new Array();
for (var i:int = 0; i < points.length - 1; i++) {
with (Math) {
// Convert coordinates from degrees to Radians
var lat1:Number = points.y * (PI / 180);
var lon1:Number = points.x * (PI / 180);
var lat2:Number = points[i + 1].y * (PI / 180);
var lon2:Number = points[i + 1].x * (PI / 180);
// Calculate the total extent of the route
var d:Number = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
// Calculate positions at fixed intervals along the route
for (var k:int = 0; k <= n; k++) {
var f:Number = (k / n);
var A:Number = sin((1 - f) * d) / sin(d);
var B:Number = sin(f * d) / sin(d);
// Obtain 3D Cartesian coordinates of each point
var x:Number = A * cos(lat1) * cos(lon1) + B * cos(lat2) * cos(lon2);
var y:Number = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
var z:Number = A * sin(lat1) + B * sin(lat2);
// Convert these to latitude/longitude
var lat:Number = atan2(z, sqrt(pow(x, 2) + pow(y, 2)));
var lon:Number= atan2(y, x);
// Create a Location (remember to convert back to degrees)
var p:MapPoint = new MapPoint(lon / (PI / 180),lat / (PI / 180));
// Add this to the array
locs.push(p);
}
}
}
return locs;
}
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
// TODO Auto-generated method stub
addLine();
}
]]>
</fx:Script>
<esri:Map >
<esri:ArcGISTiledMapServiceLayer url="http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer"/>
<esri:GraphicsLayer id="myGraphicsLayer"/>
</esri:Map>
</s:Application>