放弃百度,选择更高效的检索方式,提供工作效率
效果图
非常感谢以下两位作者的分享,也许已经找不到原作者。
方案一:http://www.cnblogs.com/siempre1091/p/3997847.html
方案二:http://shuaike.iteye.com/blog/1187603
两种方法分别采用完全不同的方式实现,各有各的优势和缺陷。
方案一的实现方法较为正常,能够正常使用
现附上方案一的整理源码:
package com.esri.viewer.tool.measure
{
import com.esri.ags.Graphic;
import com.esri.ags.Map;
import com.esri.ags.Units;
import com.esri.ags.events.DrawEvent;
import com.esri.ags.events.MapMouseEvent;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.geometry.Polygon;
import com.esri.ags.geometry.Polyline;
import com.esri.ags.layers.GraphicsLayer;
import com.esri.ags.symbols.CompositeSymbol;
import com.esri.ags.symbols.PictureMarkerSymbol;
import com.esri.ags.symbols.SimpleLineSymbol;
import com.esri.ags.symbols.SimpleMarkerSymbol;
import com.esri.ags.symbols.TextSymbol;
import com.esri.ags.tools.DrawTool;
import com.esri.ags.utils.GeometryUtil;
import com.esri.viewer.AppEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
public class Measure
{
private var lineSymbol:SimpleLineSymbol;
private var markerSymbol:SimpleMarkerSymbol;
private var normalLabel:TextSymbol;
private var endLabel:TextSymbol;
private var deleteLabel:CompositeSymbol;
private var drawLayer:GraphicsLayer;
private var drawTool:DrawTool;
private var isActive:Boolean;
private var map:Map;
//测距需要的几个全局变量
//节点数组,这是实际用于计算距离的值
private var polyArray:Array;
//用这个来标识是否应放下终点注记
private var isDraw:Boolean;
//当前节点,每次mapClick刷新
private var currentPoint:MapPoint;
//每条测距线的标识码
private var sierialId:int = 0;
private var areaGraphic:Graphic;
private var isMeausreLine:Boolean = true;
public function set IsActive(value:Boolean):void
{
isActive = value;
if(isActive)
{
InitDraw();
}
else
{
StopDraw();
}
}
public function get IsActive():Boolean
{
return isActive;
}
public function Measure(_map:Map)
{
map = _map;
}
private function InitDraw():void
{
//对于这样的类,我很喜欢使用一个函数来完成GraphicsLayer的初始化和添加
drawLayer = AddGraphicLayer("measure");
//红色细实线作为测距线
lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,0xff0000,1,2);
//小红圈作为节点
markerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE,8,0xffffff,1,0,0,0,lineSymbol);
//起点和中继点的注记符号,把用于显示的文字绑定于TEXT上
normalLabel = new TextSymbol();
normalLabel.background = true;
normalLabel.backgroundColor = 0xffffff;
normalLabel.border = true;
normalLabel.borderColor = 0x666666;
normalLabel.color = 0x666666;
normalLabel.placement = TextSymbol.PLACEMENT_START;
normalLabel.xoffset = 10;
normalLabel.textAttribute = "TEXT";
//终点的注记符号,同样绑定在Text上
endLabel = new TextSymbol();
endLabel.background = true;
endLabel.backgroundColor = 0xffffff;
endLabel.border = true;
endLabel.borderColor = 0xff0000;
endLabel.color = 0x000000;
endLabel.yoffset = 20;
endLabel.textAttribute = "TEXT";
//删除按钮的符号,用一个组合符号把按钮图标放在一个正方形的框里
deleteLabel = new CompositeSymbol();
var picSymbol:PictureMarkerSymbol = new PictureMarkerSymbol("assets/images/delete.png",16,16,15,-15);
var borderSymbol:SimpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE,18,0xffffff,1,15,-15,0,
new SimpleLineSymbol("solid",0xff0000,1,1));
deleteLabel.symbols=[borderSymbol,picSymbol];
//drawTool初始化只需注意一下我们额外需要一个DRAW_START事件
drawTool = new DrawTool(map);
drawTool.showDrawTips = false;
drawTool.graphicsLayer = drawLayer;
drawTool.lineSymbol = lineSymbol;
drawTool.addEventListener(DrawEvent.DRAW_END,drawEnd);
drawTool.addEventListener(DrawEvent.DRAW_START,drawStart);
}
//停止绘制时把绘图图层移除
private function StopDraw():void
{
if(map.getLayer("measure"))
map.removeLayer(map.getLayer("measure"));
drawTool.deactivate();
map.removeEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
polyArray = [];
}
//这是我非常喜欢的添加图层函数,初始化、添加、赋值全部打包
//如果map里有这个ID的图层,就直接放回这个图层
//如果map里还没有,就new一个,放进去再返回这个图层
private function AddGraphicLayer(layerid:String):GraphicsLayer
{
var glayer:GraphicsLayer;
if(map.getLayer(layerid)!=null)
{
glayer = map.getLayer(layerid) as GraphicsLayer;
}
else
{
glayer = new GraphicsLayer();
glayer.id = layerid;
map.addLayer(glayer);
}
return glayer;
}
//鼠标点击measure按钮,就执行这个函数开始测距
public function MeasureDistance():void
{
isMeausreLine = true;
drawTool.activate(DrawTool.POLYLINE);
drawTool.showDrawTips = true;
drawTool.toolTipStartAndLetGo="点击开始测距";
drawTool.toolTipPolyEnd="双击结束测距";
polyArray = new Array();
isDraw = true;
map.panEnabled = false;
map.addEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
}
//鼠标点击measure按钮,就执行这个函数开始测距
public function MeasureDistanceArea():void
{
isMeausreLine = false;
drawTool.activate(DrawTool.POLYGON);
drawTool.showDrawTips = true;
drawTool.toolTipStartAndLetGo="点击开始量面";
drawTool.toolTipPolyEnd="双击结束量面";
polyArray = new Array();
isDraw = true;
map.panEnabled = false;
map.addEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
}
//开始绘图时自增一下当前的测距线标识码
private function drawStart(event:DrawEvent):void
{
sierialId+=1;
}
//结束绘图时的操作,AppEvent是一个事件,目的在于通知页面测距完成了
private function drawEnd(event:DrawEvent):void
{
event.graphic.attributes = {id:sierialId};
drawTool.deactivate();
map.removeEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
map.panEnabled = true;
isDraw = false;
AppEvent.dispatch(AppEvent.MEASURE_FINISH);
}
//绘图过程中点击地图时记录节点,注意每次记录节点的过程用timer来控制
private function mapClicked(event:MapMouseEvent):void
{
currentPoint = event.mapPoint;
//0.2秒的延迟,放置节点红圈干扰drawTool
var timer:Timer = new Timer(200);
timer.addEventListener(TimerEvent.TIMER,timerEnd);
timer.start();
}
//timer里实际进行的就是距离计算和放置注记等复杂的工作
private function timerEnd(e:TimerEvent):void
{
(e.currentTarget as Timer).stop();
//拿到节点,一定要new啊,一定要new一个,否则会很悲剧
var mp:MapPoint = new MapPoint(currentPoint.x,currentPoint.y,currentPoint.spatialReference);
//往用于计算的逻辑数组里塞入这个节点
polyArray.push(mp);
//如果还在绘图当中的话,那就是放妖放下中继点或起点
if(isDraw)
{
//这样是起点,记得用TEXT来传递标注文字,用id来传递标识码
if(polyArray.length == 1)
{
drawLayer.add(new Graphic(mp,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:"起点",id:sierialId}));
}
//这样就是中继点,这是要计算距离的,注意计算时,我把polyArray实时转换成了Polygon
else
{
var lenghthStr:String = LenghthCaculator(new Polyline([polyArray],map.spatialReference));
drawLayer.add(new Graphic(mp,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:lenghthStr,id:sierialId}));
}
}
//结束绘图的话,就放终点
else
{
var deleteGr:Graphic;
if(!isMeausreLine)
{
// 倒数第二点
var totallenghth:String = LenghthCaculator(new Polyline([polyArray],map.spatialReference));
drawLayer.add(new Graphic(mp,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:totallenghth,id:sierialId}));
// 最后点,首位连接
polyArray.push(polyArray[0]);
totallenghth = LenghthCaculator(new Polyline([polyArray],map.spatialReference));
drawLayer.add(new Graphic(polyArray[0],new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总长:"+totallenghth,id:sierialId}));
// 计算面积
var tmpPolygon:Polygon = new Polygon([polyArray],map.spatialReference);
var totalAreaStr:String = AreaCaculator(tmpPolygon);
var markpoint:MapPoint = tmpPolygon.extent.center;
drawLayer.add(new Graphic(markpoint,new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总面积:"+totalAreaStr,id:sierialId}));
deleteGr = new Graphic(markpoint,deleteLabel,{id:sierialId});
}else{
var totallenghthStr:String = LenghthCaculator(new Polyline([polyArray],map.spatialReference));
drawLayer.add(new Graphic(mp,new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总长:"+totallenghthStr,id:sierialId}));
deleteGr = new Graphic(mp,deleteLabel,{id:sierialId});
}
//这是要放删除按钮,就是一个graphic啦,但是需要绑定一下Click事件。
deleteGr.toolTip = "删除";
deleteGr.buttonMode = true;
deleteGr.addEventListener(MouseEvent.CLICK,deleteHandler);
drawLayer.add(deleteGr);
}
}
//删除的时候根据标识码来删除对应测距线中的元素
private function deleteHandler(event:MouseEvent):void
{
var id:int = (event.currentTarget as Graphic).attributes.id;
var deletArray:Array = new Array();
for each(var line:Graphic in drawLayer.graphicProvider)
{
if(line.attributes.id == id)
{
deletArray.push(line);
}
}
for each(var graphic:Graphic in deletArray)
{
drawLayer.remove(graphic);
}
//如果删除的是最后一条测距线,就把这个测距图层移除,并且让测距功能关闭,减少资源占用
if((drawLayer.graphicProvider as ArrayCollection).length == 0)
this.IsActive = false;
}
//距离计算,注意使用的函数,另外距离比较短的时候单位可以变为米
private function LenghthCaculator(polyline:Polyline):String
{
var lenghth:Number = GeometryUtil.geodesicLengths([polyline],Units.METERS)[0];
if(lenghth>1000)
return (lenghth/1000).toFixed(2)+"公里";
else
return lenghth.toFixed(2)+"米";
}
private function AreaCaculator(polygon:Polygon):String
{
var lenghth:Number = GeometryUtil.geodesicAreas([polygon],Units.SQUARE_METERS)[0];
if(lenghth>=1000000)
return (lenghth/1000000).toFixed(2)+"平方千米";
else
return lenghth.toFixed(2)+"平方米";
}
}
}
调用方式如下:
Measure measureTool = new Measure(map)
if(!measureTool.IsActive)
measureTool.IsActive = true;
measureTool.MeasureDistance();
方案一的缺陷是长度计算方式只支持地理坐标,不支持平面坐标。在释放操作过程中没有进行完全释放内容。
方案二源码如下
package com.esri.viewer.tool
{
import com.esri.ags.Map;
import com.esri.ags.events.MapMouseEvent;
import com.esri.ags.tasks.GeometryServiceSingleton;
import com.esri.viewer.AppEvent;
import com.esri.viewer.ViewerContainer;
import com.esri.viewer.tool.measure.MeasureLengths;
import flash.events.MouseEvent;
import flash.utils.clearInterval;
import flash.utils.setInterval;
public class ToolWidget
{
public static var interval:Number=0;
public static var measureGrArr:Array=[]; //地图上绘制的测量线的图层
private var measureLength:MeasureLengths;
private var map:Map;
// 当前执行的工具
private var tool:String;
public function ToolWidget(map:Map)
{
this.map=map;
}
public function activeTool(tool:String, status:String):void
{
this.tool = tool;
switch (this.tool)
{
case ViewerContainer.MEASURE_LENGTH:
{
lengthHandler();
break;
}
}
}
private function lengthHandler():void
{
//setMapAction(DrawTool.POLYLINE, LocaleResource.strings("iframe_label_length"), null, _compute.drawEndHandler);_compute.mapclick _compute.measureFinish
measureLength=new MeasureLengths(GeometryServiceSingleton.instance.url, map);
map.addEventListener(MapMouseEvent.MAP_CLICK, clickLength);
map.addEventListener(MouseEvent.DOUBLE_CLICK, doubleclickLength);
map.doubleClickEnabled=true;
map.openHandCursorVisible=false;
}
//消除单击和双击使劲啊的冲突
private function clickLength(event:MouseEvent):void
{
clearInterval(interval);
interval=setInterval(measureLength.mapclick, 200, event);
}
//消除单击和双击使劲啊的冲突
private function doubleclickLength(event:MouseEvent):void
{
clearInterval(interval);
measureLength.measureFinish(event);
measureLengthFinish(null);
}
//取测量点完毕
private function measureLengthFinish(event:AppEvent):void
{
//取消测量点击的双击单击事件
map.removeEventListener(MapMouseEvent.MAP_CLICK, clickLength);
map.removeEventListener(MouseEvent.DOUBLE_CLICK, doubleclickLength);
}
}
}
package com.esri.viewer.tool.measure
{
import flash.events.Event;
import mx.containers.Canvas;
import mx.events.FlexEvent;
import spark.components.Label;
public class Infosym extends Canvas
{
public function Infosym()
{
super();
this.addEventListener(FlexEvent.CREATION_COMPLETE, init);
}
private function init(event:Event):void
{
var label:Label=new Label();
this.addChild(label);
label.height=16;
label.setStyle("borderColor", "red");
label.setStyle("color", "red");
label.setStyle("textAlign", "center");
label.text=super.data.toString() + "公里";
}
}
}
package com.esri.viewer.tool.measure
{
import flash.events.Event;
import mx.containers.Canvas;
import mx.events.FlexEvent;
import spark.components.Label;
public class InfosymStart extends Canvas
{
public function InfosymStart()
{
super();
this.addEventListener(FlexEvent.CREATION_COMPLETE, init);
}
private function init(event:Event):void
{
var label:Label=new Label();
this.addChild(label);
label.height=16;
label.setStyle("borderColor", "red");
label.setStyle("color", "red");
label.setStyle("textAlign", "center");
label.text="起点";
label.width=45;
}
}
}
package com.esri.viewer.tool.measure
{
import flash.events.Event;
public class MeasureEvent extends Event
{
public static var MEASURE_FINISH:String="measurefinish";
private var _data:Object;
public function get data():Object{
return _data;
}
public function MeasureEvent(type:String,data:Object=null,bubbles:Boolean=false, cancelable:Boolean=false)
{
this._data=data;
super(type, bubbles, cancelable);
}
}
}
package com.esri.viewer.tool.measure
{
import com.esri.ags.Graphic;
import com.esri.ags.Map;
import com.esri.ags.components.supportClasses.InfoPlacement;
import com.esri.ags.events.GeometryServiceEvent;
import com.esri.ags.events.MapMouseEvent;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.geometry.Polyline;
import com.esri.ags.layers.GraphicsLayer;
import com.esri.ags.symbols.InfoSymbol;
import com.esri.ags.symbols.PictureMarkerSymbol;
import com.esri.ags.symbols.SimpleLineSymbol;
import com.esri.ags.symbols.SimpleMarkerSymbol;
import com.esri.ags.tasks.GeometryService;
import com.esri.ags.tasks.supportClasses.DistanceParameters;
import com.esri.viewer.AppEvent;
import com.esri.viewer.tool.ToolWidget;
import com.esri.viewer.tool.measure.Infosym;
import com.esri.viewer.tool.measure.InfosymStart;
import flash.events.MouseEvent;
import flash.utils.clearInterval;
import flash.utils.setInterval;
import mx.core.ClassFactory;
import mx.formatters.NumberFormatter;
/**
* 测量工具类,每测量一次初始化该类
* */
public class MeasureLengths
{
private const _service:GeometryService=new GeometryService();
private var map:Map;
private var measureGraphicLayer:GraphicsLayer=new GraphicsLayer(); //准测量图层
private var measurePoiintGraphicLayer:GraphicsLayer=new GraphicsLayer(); //真实测量图层
private var lineGraphic:Graphic=new Graphic(); //准测量图形
private var realLineGraphic:Graphic=new Graphic(); //真实测量图形
private var pointArr:Array=[]; //保存所有的测量点
private var distanceArr:Array=[]; //保存所有的测量距离
private var flag:Boolean=false; //判断是否是最后一个点
private var tmpGraphic:Graphic = null;
private var clickPoint:MapPoint;
public function MeasureLengths(url:String, map:Map=null)
{
_service.url=url;
this.map=map;
measureGraphicLayer.add(lineGraphic);
measurePoiintGraphicLayer.add(realLineGraphic);
map.addLayer(measureGraphicLayer);
map.addLayer(measurePoiintGraphicLayer);
_service.addEventListener(GeometryServiceEvent.DISTANCE_COMPLETE, distancecomplete);
map.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveFun);
}
public function disposeMeasureLengths():void
{
measureGraphicLayer.remove(lineGraphic);
measurePoiintGraphicLayer.remove(realLineGraphic);
map.removeLayer(measureGraphicLayer);
map.removeLayer(measurePoiintGraphicLayer);
_service.removeEventListener(GeometryServiceEvent.DISTANCE_COMPLETE, distancecomplete);
map.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveFun);
}
public function mapclick(event:MapMouseEvent):void
{
clickPoint=event.mapPoint;
pointArr.push(clickPoint);
var pointSysmbol:SimpleMarkerSymbol=new SimpleMarkerSymbol("circle", 12, 0xFF0000, 0.65);
var clickPointGraphic:Graphic=new Graphic(clickPoint, pointSysmbol);
measurePoiintGraphicLayer.add(clickPointGraphic);
if (pointArr.length > 1)
{
realGraphicLine(pointArr);
}
else
{
measurePoiintGraphicLayer.add(createStartLable(pointArr[0] as MapPoint));
}
clearInterval(ToolWidget.interval);
}
private function mouseMoveFun(moveEvent:MouseEvent):void
{
var movePoint:MapPoint=map.toMapFromStage(moveEvent.stageX, moveEvent.stageY);
if(tmpGraphic)
{
measurePoiintGraphicLayer.remove(tmpGraphic);
}
if(pointArr.length > 0)
{
if(tmpGraphic)
{
tmpGraphic = null;
}
//根据movePoint准测量点 画准测量线
graphicLine(movePoint, clickPoint);
}
else
{
tmpGraphic?tmpGraphic.geometry = movePoint:tmpGraphic = createStartLable(movePoint);
measurePoiintGraphicLayer.add(tmpGraphic);
}
}
private function createStartLable(movePoint:MapPoint):Graphic
{
var infosymbol:InfoSymbol=new InfoSymbol();
infosymbol.infoRenderer=new ClassFactory(InfosymStart);
infosymbol.containerStyleName="infosymbolvenues";
infosymbol.infoPlacement=InfoPlacement.RIGHT;
return new Graphic(movePoint, infosymbol);
}
//画测量点完毕,开始测量
public function measureFinish(event:MouseEvent):void
{
if (pointArr.length > 0)
{
var finishPoint:MapPoint=map.toMapFromStage(event.stageX, event.stageY);
pointArr.push(finishPoint);
var pointSysmbol:SimpleMarkerSymbol=new SimpleMarkerSymbol("circle", 12, 0xFF0000, 0.65);
var clickPointGraphic:Graphic=new Graphic(finishPoint, pointSysmbol);
measurePoiintGraphicLayer.add(clickPointGraphic);
var picSysmbol:PictureMarkerSymbol=new PictureMarkerSymbol("assets/images/delete.png", 16, 16,-20);
var finishGraphic:Graphic=new Graphic(finishPoint,picSysmbol);
measurePoiintGraphicLayer.add(finishGraphic);
finishGraphic.addEventListener(MouseEvent.CLICK, clearCurrentLayer);
if (pointArr.length > 1)
{
realGraphicLine(pointArr);
}
//移除画准测量线的事件
map.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveFun);
//清空准测量图层
measureGraphicLayer.remove(lineGraphic);
//派发一个测量完的事件;移除map点击事件
AppEvent.dispatch(AppEvent.MEASURE_FINISH, "");
//开始测量
flag=true;
measureFun(pointArr);
}
}
//画真实测量线
private function realGraphicLine(pointArr:Array):void
{
var realLineSysmbol:SimpleLineSymbol=new SimpleLineSymbol("solid", 0xFF0000, 0.65, 3);
var realLine:Polyline=new Polyline([pointArr], map.spatialReference);
realLineGraphic.geometry=realLine;
realLineGraphic.symbol=realLineSysmbol;
//测量两点距离
measureFun(pointArr);
}
//画准测量线
private function graphicLine(movePoint:MapPoint, pointArrLast:MapPoint):void
{
var lineSysmbol:SimpleLineSymbol=new SimpleLineSymbol("solid", 0xFA8072, 0.6, 3);
var paths:Array=[pointArrLast,movePoint];
var line:Polyline=new Polyline([paths], map.spatialReference);
lineGraphic.geometry=line;
lineGraphic.symbol=lineSysmbol;
}
//删除地图上的测量图层
private function clearCurrentLayer(event:MouseEvent):void
{
map.removeLayer(event.currentTarget.parent);
}
//距离测量
private function measureFun(pointArr:Array):void
{
if (pointArr.length > 1)
{
var distanceParmers:DistanceParameters=new DistanceParameters();
distanceParmers.distanceUnit=GeometryService.UNIT_STATUTE_MILE; //英里
distanceParmers.geometry1=pointArr[pointArr.length - 2];
distanceParmers.geometry2=pointArr[pointArr.length - 1];
distanceParmers.geodesic=true;
_service.distance(distanceParmers);
}
}
//两点测量完毕
private function distancecomplete(event:GeometryServiceEvent):void
{
var distanceObj:Object=event.result;
var numberFormatter:NumberFormatter=new NumberFormatter();
numberFormatter.precision=3;
var kilometre:Number=(distanceObj as Number) * 1.6;
var distanceNUm:Number=Math.round(kilometre * 1000) / 1000;
if (distanceArr.length > 0)
{
distanceNUm=distanceNUm + distanceArr[distanceArr.length - 1];
}
distanceArr.push(distanceNUm);
var graphic:Graphic;
if (!flag)
{
graphic=new Graphic(pointArr[pointArr.length - 1] as MapPoint, createInfoSymbol(), numberFormatter.format(distanceNUm));
//graphic.autoMoveToTop=true;
}
else
{
graphic=new Graphic(pointArr[pointArr.length - 1] as MapPoint, createInfoSymbol(), "总长:" + numberFormatter.format(distanceNUm));
//graphic.autoMoveToTop=true;
}
measurePoiintGraphicLayer.add(graphic);
}
private function createInfoSymbol():InfoSymbol
{
var infosymbol:InfoSymbol=new InfoSymbol();
infosymbol.infoRenderer=new ClassFactory(Infosym);
infosymbol.containerStyleName="infosymbolvenues";
infosymbol.infoPlacement=InfoPlacement.RIGHT;
return infosymbol;
}
}
}
方案二缺陷就更多了,几乎都是无法使用的,尤其是鼠标双击事件冲突、鼠标点击和移动事件冲突,坑了很久,所以还是放弃。
最后综合两种方案得出完整的解决方案,首先利用方案一的DrawTool控制交互,用方案二的GeometryServiceEvent计算长度面积。后续可以自己进一步丰富,
如下是新的完整的代码。支持长度和面积的测量
package com.esri.viewer.tool.measure
{
import com.esri.ags.Graphic;
import com.esri.ags.Map;
import com.esri.ags.Units;
import com.esri.ags.events.DrawEvent;
import com.esri.ags.events.GeometryServiceEvent;
import com.esri.ags.events.MapMouseEvent;
import com.esri.ags.geometry.MapPoint;
import com.esri.ags.geometry.Polygon;
import com.esri.ags.geometry.Polyline;
import com.esri.ags.layers.GraphicsLayer;
import com.esri.ags.symbols.CompositeSymbol;
import com.esri.ags.symbols.PictureMarkerSymbol;
import com.esri.ags.symbols.SimpleLineSymbol;
import com.esri.ags.symbols.SimpleMarkerSymbol;
import com.esri.ags.symbols.TextSymbol;
import com.esri.ags.tasks.GeometryService;
import com.esri.ags.tasks.supportClasses.AreasAndLengthsParameters;
import com.esri.ags.tasks.supportClasses.DistanceParameters;
import com.esri.ags.tools.DrawTool;
import com.esri.ags.utils.GeometryUtil;
import com.esri.viewer.AppEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.utils.Timer;
import mx.collections.ArrayCollection;
import mx.formatters.NumberFormatter;
public class Measure
{
private var lineSymbol:SimpleLineSymbol;
private var markerSymbol:SimpleMarkerSymbol;
private var normalLabel:TextSymbol;
private var endLabel:TextSymbol;
private var deleteLabel:CompositeSymbol;
private var drawLayer:GraphicsLayer;
private var drawTool:DrawTool;
private var isActive:Boolean;
private var map:Map;
//测距需要的几个全局变量
//节点数组,这是实际用于计算距离的值
private var polyArray:Array;
//用这个来标识是否应放下终点注记
private var isDraw:Boolean;
//当前节点,每次mapClick刷新
private var currentPoint:MapPoint;
//每条测距线的标识码
private var sierialId:int = 0;
private var areaGraphic:Graphic;
private var isMeasureLine:Boolean = true;
private const _service:GeometryService=new GeometryService();
//保存所有的测量距离
private var distanceArr:Array=[];
private var areaCenterPoint:MapPoint;
public function set IsActive(value:Boolean):void
{
isActive = value;
if(isActive)
{
InitDraw();
}
else
{
StopDraw();
}
}
public function get IsActive():Boolean
{
return isActive;
}
public function Measure(url:String, _map:Map)
{
map = _map;
_service.url=url;
}
private function InitDraw():void
{
//对于这样的类,我很喜欢使用一个函数来完成GraphicsLayer的初始化和添加
drawLayer = AddGraphicLayer("measure");
//红色细实线作为测距线
lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,0xff0000,1,2);
//小红圈作为节点
markerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE,8,0xffffff,1,0,0,0,lineSymbol);
//起点和中继点的注记符号,把用于显示的文字绑定于TEXT上
normalLabel = new TextSymbol();
normalLabel.background = true;
normalLabel.backgroundColor = 0xffffff;
normalLabel.border = true;
normalLabel.borderColor = 0x666666;
normalLabel.color = 0x666666;
normalLabel.placement = TextSymbol.PLACEMENT_START;
normalLabel.xoffset = 10;
normalLabel.textAttribute = "TEXT";
//终点的注记符号,同样绑定在Text上
endLabel = new TextSymbol();
endLabel.background = true;
endLabel.backgroundColor = 0xffffff;
endLabel.border = true;
endLabel.borderColor = 0xff0000;
endLabel.color = 0x000000;
endLabel.yoffset = 20;
endLabel.textAttribute = "TEXT";
//删除按钮的符号,用一个组合符号把按钮图标放在一个正方形的框里
deleteLabel = new CompositeSymbol();
var picSymbol:PictureMarkerSymbol = new PictureMarkerSymbol("assets/images/delete.png",16,16,15,-15);
var borderSymbol:SimpleMarkerSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE,18,0xffffff,1,15,-15,0,
new SimpleLineSymbol("solid",0xff0000,1,1));
deleteLabel.symbols=[borderSymbol,picSymbol];
//drawTool初始化只需注意一下我们额外需要一个DRAW_START事件
drawTool = new DrawTool(map);
drawTool.showDrawTips = false;
drawTool.graphicsLayer = drawLayer;
drawTool.lineSymbol = lineSymbol;
drawTool.addEventListener(DrawEvent.DRAW_END,drawEnd);
drawTool.addEventListener(DrawEvent.DRAW_START,drawStart);
}
//停止绘制时把绘图图层移除
private function StopDraw():void
{
if(map.getLayer("measure"))
map.removeLayer(map.getLayer("measure"));
drawTool.deactivate();
map.removeEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
polyArray = [];
isMeasureLine?_service.removeEventListener(GeometryServiceEvent.AREAS_AND_LENGTHS_COMPLETE, distanceComplete):
_service.addEventListener(GeometryServiceEvent.AREAS_AND_LENGTHS_COMPLETE, areaComplete);
}
//这是我非常喜欢的添加图层函数,初始化、添加、赋值全部打包
//如果map里有这个ID的图层,就直接放回这个图层
//如果map里还没有,就new一个,放进去再返回这个图层
private function AddGraphicLayer(layerid:String):GraphicsLayer
{
var glayer:GraphicsLayer;
if(map.getLayer(layerid)!=null)
{
glayer = map.getLayer(layerid) as GraphicsLayer;
}
else
{
glayer = new GraphicsLayer();
glayer.id = layerid;
map.addLayer(glayer);
}
return glayer;
}
//鼠标点击measure按钮,就执行这个函数开始测距
public function MeasureDistance():void
{
isMeasureLine = true;
drawTool.activate(DrawTool.POLYLINE);
drawTool.showDrawTips = true;
drawTool.toolTipStartAndLetGo="点击开始测距";
drawTool.toolTipPolyEnd="双击结束测距";
polyArray = new Array();
isDraw = true;
map.panEnabled = false;
map.addEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
_service.addEventListener(GeometryServiceEvent.DISTANCE_COMPLETE, distanceComplete);
}
//鼠标点击measure按钮,就执行这个函数开始测距
public function MeasureDistanceArea():void
{
isMeasureLine = false;
drawTool.activate(DrawTool.POLYGON);
drawTool.showDrawTips = true;
drawTool.toolTipStartAndLetGo="点击开始量面";
drawTool.toolTipPolyEnd="双击结束量面";
polyArray = new Array();
isDraw = true;
map.panEnabled = false;
map.addEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
_service.addEventListener(GeometryServiceEvent.AREAS_AND_LENGTHS_COMPLETE, areaComplete);
}
//距离测量
private function measureLengthFun(pointArr:Array):void
{
if (pointArr.length > 1)
{
var distanceParmers:DistanceParameters=new DistanceParameters();
distanceParmers.distanceUnit=GeometryService.UNIT_METER; //英里
distanceParmers.geometry1=pointArr[pointArr.length - 2];
distanceParmers.geometry2=pointArr[pointArr.length - 1];
distanceParmers.geodesic=true;
_service.distance(distanceParmers);
}
}
//面积测量
private function measureAreaFun(polygons:Array):void
{
var areaParmers:AreasAndLengthsParameters=new AreasAndLengthsParameters();
areaParmers.lengthUnit=GeometryService.UNIT_METER;
areaParmers.areaUnit=GeometryService.UNIT_SQUARE_METERS;
areaParmers.polygons =polygons;
_service.areasAndLengths(areaParmers);
}
//开始绘图时自增一下当前的测距线标识码
private function drawStart(event:DrawEvent):void
{
sierialId+=1;
}
//结束绘图时的操作,AppEvent是一个事件,目的在于通知页面测距完成了
private function drawEnd(event:DrawEvent):void
{
event.graphic.attributes = {id:sierialId};
drawTool.deactivate();
map.removeEventListener(MapMouseEvent.MAP_CLICK,mapClicked);
map.panEnabled = true;
isDraw = false;
AppEvent.dispatch(AppEvent.MEASURE_FINISH);
}
//绘图过程中点击地图时记录节点,注意每次记录节点的过程用timer来控制
private function mapClicked(event:MapMouseEvent):void
{
currentPoint = event.mapPoint;
//0.2秒的延迟,放置节点红圈干扰drawTool
var timer:Timer = new Timer(200);
timer.addEventListener(TimerEvent.TIMER,timerEnd);
timer.start();
}
//timer里实际进行的就是距离计算和放置注记等复杂的工作
private function timerEnd(e:TimerEvent):void
{
(e.currentTarget as Timer).stop();
//拿到节点,一定要new啊,一定要new一个,否则会很悲剧
var mp:MapPoint = new MapPoint(currentPoint.x,currentPoint.y,currentPoint.spatialReference);
//往用于计算的逻辑数组里塞入这个节点
polyArray.push(mp);
//如果还在绘图当中的话,那就是放妖放下中继点或起点
if(isDraw)
{
//这样是起点,记得用TEXT来传递标注文字,用id来传递标识码
if(polyArray.length == 1)
{
drawLayer.add(new Graphic(mp,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:"起点",id:sierialId}));
}
else
{
measureLengthFun(polyArray);
}
}
else
{
//结束绘图的话,就放终点
var deleteGr:Graphic;
if(!isMeasureLine)
{
// 倒数第二点
measureLengthFun(polyArray);
// 最后点,首位连接
var tmpPolygon:Polygon = new Polygon([polyArray],map.spatialReference);
areaCenterPoint = tmpPolygon.extent.center;
measureAreaFun([new Polygon([polyArray],map.spatialReference)]);
deleteGr = new Graphic(areaCenterPoint,deleteLabel,{id:sierialId});
}else{
measureLengthFun(polyArray);
deleteGr = new Graphic(mp,deleteLabel,{id:sierialId});
}
//这是要放删除按钮,就是一个graphic啦,但是需要绑定一下Click事件。
deleteGr.toolTip = "删除";
deleteGr.buttonMode = true;
deleteGr.addEventListener(MouseEvent.CLICK,deleteHandler);
drawLayer.add(deleteGr);
}
}
private function distanceComplete(event:GeometryServiceEvent):void
{
var distanceObj:Object=event.result;
var kilometre:Number=(distanceObj as Number) * 1.6;
var distanceNum:Number=Math.round(kilometre * 1000) / 1000;
if (distanceArr.length > 0)
{
distanceNum=distanceNum + distanceArr[distanceArr.length - 1];
}
distanceArr.push(distanceNum);
var lengthStr:String =distanceNum>1000?(distanceNum/1000).toFixed(2)+"千米":distanceNum.toFixed(2)+"米";
if(isDraw)
{
//这样是起点,记得用TEXT来传递标注文字,用id来传递标识码
if(polyArray.length == 1)
{
drawLayer.add(new Graphic(currentPoint,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:"起点",id:sierialId}));
}
else
{
drawLayer.add(new Graphic(currentPoint,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:lengthStr,id:sierialId}));
}
}
//结束绘图的话,就放终点
else
{
if(isMeasureLine)
{
drawLayer.add(new Graphic(currentPoint,new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总长:"+lengthStr,id:sierialId}));
}
else
{
drawLayer.add(new Graphic(currentPoint,new CompositeSymbol([markerSymbol,normalLabel]),{TEXT:lengthStr,id:sierialId}));
}
}
}
private function areaComplete(event:GeometryServiceEvent):void
{
var area:Number = event.result.areas[0]; //event.arealengths.areas[0];
var length:Number = event.result.lengths[0]; // or (event.result as Array)[0]; //event.arealengths.lengths[0];
var lengthStr:String =length>1000?(length/1000).toFixed(2)+"千米":length.toFixed(2)+"米";
drawLayer.add(new Graphic(polyArray[0],new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总周长:"+lengthStr,id:sierialId}));
var areaStr:String = area>=1000000?(area/1000000).toFixed(2)+"平方千米":area.toFixed(2)+"平方米";
drawLayer.add(new Graphic(areaCenterPoint,new CompositeSymbol([markerSymbol,endLabel]),{TEXT:"总面积:"+areaStr,id:sierialId}));
}
//删除的时候根据标识码来删除对应测距线中的元素
private function deleteHandler(event:MouseEvent):void
{
var id:int = (event.currentTarget as Graphic).attributes.id;
var deletArray:Array = new Array();
for each(var line:Graphic in drawLayer.graphicProvider)
{
if(line.attributes.id == id)
{
deletArray.push(line);
}
}
for each(var graphic:Graphic in deletArray)
{
drawLayer.remove(graphic);
}
//如果删除的是最后一条测距线,就把这个测距图层移除,并且让测距功能关闭,减少资源占用
if((drawLayer.graphicProvider as ArrayCollection).length == 0)
this.IsActive = false;
}
}
}