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; }