var n = 32;
var d = 2 * asin(sqrt(pow((sin((lat1 - lat2) / 2)), 2) + cos(lat1) * cos(lat2) * pow((sin((lon1 - lon2) / 2)), 2)));
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);
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;
}
<?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>
override public function draw(sprite:Sprite, geometry:Geometry, attributes:Object, map:Map):void{...}var path:Array, screenPath:Array;
var screenPaths:Array = [];
for each (path in line.paths) {
screenPath = [];
for (var i:int = 0; i < path.length; i++){
screenPath.push(new Point(toScreenX(map,path.x),toScreenY(map,path.y)));
}
screenPaths.push(screenPath);
}public function drawUniformCubicPath(points:Array, handleLengthRatio:Number = 3, stipplePattern:uint = 0xFFFFFFFF):void {
var i:int;
var first:Point, mid:Point, last:Point, a:Point, b:Point, c:Point, d:Point, firstLen:Number, midLen:Number,lastLen:Number;
var vec1:Point, vec2:Point, control1:Point, control2:Point;
a = points[0];
current.x = a.x;
current.y = a.y;
b = points[1];
c = points[2]
first = a.subtract(b);
firstLen = first.length;
first.normalize(1.0);
mid = b.subtract(c);
midLen = mid.length;
mid.normalize(1.0);
vec1 = new Point(-first.x, -first.y);
vec1.normalize(firstLen / handleLengthRatio);
vec1.offset(a.x, a.y);
vec2 = Point.interpolate(first, mid, 0.5);
vec2.normalize(firstLen / handleLengthRatio);
control1 = new Point(0, 0);
control2 = new Point(vec2.x, vec2.y);
control2.offset(b.x, b.y);
this.cubicCurveToPoint(vec1,control2,b,stipplePattern);
for (i = 3; i < points.length; i++) {
d = points;
last = c.subtract(d);
lastLen = last.length;
last.normalize(1.0);
vec1 = vec2;
vec1.x = -vec1.x; vec1.y = -vec1.y;
vec1.normalize(midLen / handleLengthRatio /** ratio*/);
vec2 = Point.interpolate(mid, last, 0.5);
vec2.normalize(midLen / handleLengthRatio /** (1.0 - ratio)*/);
control1.x = vec1.x;
control1.y = vec1.y;
control1.offset(b.x, b.y);
control2.x = vec2.x;
control2.y = vec2.y;
control2.offset(c.x, c.y);
this.cubicCurveToPoint(control1, control2, c, stipplePattern);
a = b;
b = c;
c = d;
first = mid;
mid = last;
firstLen = midLen;
midLen = lastLen;
}
vec2.x = -vec2.x;
vec2.y = -vec2.y;
vec2.offset(b.x, b.y);
mid.normalize(midLen / handleLengthRatio);
mid.offset(c.x, c.y);
this.cubicCurveToPoint(vec2,mid,c,stipplePattern);
}
public function cubicCurveToPoint(control1:Point, control2:Point, anchor:Point, stipplePattern:uint = 0xFFFFFFFF):void {
var cx:Number = 3 * (control1.x - current.x);
var bx:Number = 3 * (control2.x - control1.x) - cx;
var ax:Number = anchor.x - current.x - cx - bx;
var cy:Number = 3 * (control1.y - current.y);
var by:Number = 3 * (control2.y - control1.y) - cy;
var ay:Number = anchor.y - current.y - cy - by;
var xPos:Number;
var yPos:Number;
var tSquared:Number = 0;
var tCubed:Number = 0;
var step:Number = 1.0 / Math.max(Math.abs(current.x-anchor.x),Math.abs(current.y-anchor.y));
var phase:uint = 0;
var move:Boolean = false;
for (var t:Number = 0; t <= 1; t += step) {
//skip the blank spots as defined by the dashes binary type
while ((stipplePattern & (1 << (31-phase))) == 0) {
move = true;
t += step;
phase = (phase + 1) % 32;
}
tSquared = t * t;
tCubed = tSquared * t;
xPos = ax * tCubed + bx * tSquared + cx * t + current.x;
yPos = ay * tCubed + by * tSquared + cy * t + current.y;
if(move)
graphics.moveTo(xPos, yPos);
else
graphics.lineTo(xPos, yPos);
move = false;
phase = (phase + 1) % 32;
}
current.x = anchor.x;
current.y = anchor.y;
}