以上所有具体操作可参考桌面帮助中的内容,如果安装了ArcTutor,还会有Linear Referencing Tutorial的PDF可供学习。
首先来思考几个LinearReferencing的应用场景(后面会对这些场景一一实现):
1、对于某条公路上的一点进行Identify操作,要求返回该点处在公路上的里程值;
2、输入起始和终止桩号,要求将其间的路段显示在地图上;
3、类似GoogleMap的交通流量地图。
要将这些功能在ArcGISServer中“落地”,首先决定采用哪种开发方式:客户端API还是Web ADF?两者皆可。但是稍加思考,两种方式的API中都没有提供有关Linear Referencing的解决办法。所以,Linear Referencing在ArcGIS Server中的实现原理还是得依靠ArcObjects了。
ArcObjects中,Linear Referencing的功能在ESRI.ArcGIS.Location库中,请见:http://resources.esri.com/help/9.3/ArcGISEngine/dotnet/10745641-b5e3-4600-979f-cdf9d2bbc7ce.htm#About%20linear%20referencing。
为了实现上述功能,其中比较关键的有两个类:RouteLocator和RouteEventSource。RouteLocator的IRouteLocator2接口有Identify和Locate两个方法,前者可根据地理坐标识别出Route上的位置(M值);后者可根据M值来定位出Route上相应的Geometry。如果已有EventTable,需要对上面的每个Event进行定位,除了重复使用IRouteLocator2.Locate方法(或者LocateRow方法)外,也可通过构建RouteEventSource来实现。它是一种特殊的FeatureClass(继承自FeatureClass),其中的每个Feature代表了EventTable中的一个Event,而这个Feature的Shape字段内容则是根据EventTable和Route
FeatureClass,利用Dynamic
Segmentation技术实时计算出来的。
关于如何获得可用的IRouteLocator2接口和RouteEventSource,文档中已经讲的很清楚;另外ESRI.ArcGIS.Location中还提供了实现ArcMap中LinearReferencing里所有功能的接口,有兴趣的朋友可以仔细阅读。
现在来实现上面提到的3个功能。
1、对于某条公路上的一点进行Identify操作,要求返回该点在公路上的桩号值:
以Silverlight API为例。为了在服务器端使用ArcObjects,在Asp.net工程中添加一个名为LinearRef的Silverlight-Enabled WCF Service,在LinearRef.svc.cs文件中添加以下代码:
- namespace RoadDycSeg.Web.WCF
- {
- [ServiceContract(Namespace = "")]
- [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
- public class LinearRef
- {
- public IRouteLocator2 pRtLocator = null;
- ESRI.ArcGIS.Server.IServerObjectManager pSOM = null;
- public ESRI.ArcGIS.Server.IServerContext pServerContext = null;
- public LinearRef()
- {
- ESRI.ArcGIS.ADF.Identity identity = new ESRI.ArcGIS.ADF.Identity("ArcGISWebServices", "yourpassword", "");
- ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsconn = new ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("localhost", identity);
- agsconn.Connect();
- pSOM = agsconn.ServerObjectManager;
- pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");
- ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContext.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
- ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
- ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
- IFeatureClass pFC = (pMap.get_Layer(2) as ESRI.ArcGIS.Carto.IFeatureLayer).FeatureClass;
- //create the RouteLocator
- IDataset dS = (IDataset)pFC; // A polylineM feature class.
- IName name = dS.FullName;
- IRouteLocatorName rtLocatorName = pServerContext.CreateObject("esriLocation.RouteMeasureLocatorName") as IRouteLocatorName;
- rtLocatorName.RouteFeatureClassName = name;
- rtLocatorName.RouteIDFieldName = "道路编码";
- rtLocatorName.RouteMeasureUnit = esriUnits.esriUnknownUnits;
- name = (IName)rtLocatorName;
- pRtLocator = (IRouteLocator2)name.Open();
- pServerContext.ReleaseContext();
- }
- // Add more operations here and mark them with [OperationContract]
- [OperationContract]
- /// <summary>
- /// used by the client Identify operation and return the result
- /// </summary>
- /// <param name="mapX">the x coords of the mouseclick in map units</param>
- /// <param name="mapY">the y coords of the mouseclick in map units</param>
- /// <param name="resolution">the map distance of 1 pixel</param>
- /// <returns>a identifyresult class defined in Classes.cs. If no result, mvalue=x=y=-1</returns>
- public IdentifyResult Identify(double mapX, double mapY, double resolution)
- {
- pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");
- IdentifyResult ir = new IdentifyResult("", -1, -1, -1);
- IPoint pPoint = pServerContext.CreateObject("esriGeometry.Point") as IPoint;
- pPoint.PutCoords(mapX, mapY);
- IEnvelope pEnvelope = pPoint.Envelope;
- pEnvelope.Expand(10 * resolution, 10 * resolution, false);
- IEnumRouteIdentifyResult pEnumResult = pRtLocator.Identify(pEnvelope, "");
- pEnumResult.Reset();
- //only get the first result
- if (pEnumResult.Count > 0)
- {
- IRouteLocation pRL = null;
- IFeature pF = null;
- pEnumResult.Next(out pRL, out pF);
- IRouteMeasurePointLocation pRMPL = pRL as IRouteMeasurePointLocation;
- //retrieve the location geometry by calling LOCATE method
- IRouteLocation routeLocation = pServerContext.CreateObject("esriLocation.RouteMeasurePointLocation") as IRouteLocation;
- routeLocation.MeasureUnit = esriUnits.esriUnknownUnits;
- routeLocation.RouteID = pRL.RouteID;
- routeLocation.LateralOffset = 0;
- IRouteMeasurePointLocation rMPointLoc = (IRouteMeasurePointLocation)routeLocation;
- rMPointLoc.Measure = pRMPL.Measure;
- IGeometry geom;
- esriLocatingError locError;
- pRtLocator.Locate((IRouteLocation)rMPointLoc, out geom, out locError);
- ir.RouteID = pRL.RouteID.ToString();
- ir.MValue = pRMPL.Measure;
- ir.X = (geom as IPointCollection5).get_Point(0).X;
- ir.Y = (geom as IPointCollection5).get_Point(0).Y;
- }
- pServerContext.ReleaseContext();
- return ir;
- }
其中,IdentifyResult是自定义的一个类,用来存储查询的结果:
- namespace RoadDycSeg.Web.WCF
- {
- /// <summary>
- /// the result of Identify operation
- /// </summary>
- [DataContract]
- public class IdentifyResult
- {
- [DataMember]
- public string RouteID {get;set;}
- [DataMember]
- public double MValue {get;set;}
- [DataMember]
- public double X {get;set;}
- [DataMember]
- public double Y {get;set;}
- public IdentifyResult(string routeid, double mvalue, double x, double y)
- {
- RouteID = routeid;
- MValue = mvalue;
- X = x;
- Y = y;
- }
- }
- }
Identify方法需要传入3个参数,一个点的地理坐标以及当前地图的Resolution,后者用来生成一个10像素的缓冲区,方便用户的点击操作;其中利用IRouteLocator2.Identify方法获得M值,据此利用 IRouteLocator2.Locate方法获得精确落在公路上的点的Geometry,用以显示在客户端;如果用户点击的点距离公路较远,则IRouteLocator2.Identify结果为空,返回的IdentifyResult中M值为-1。
在Silverlight工程中,Add Service Reference,找到刚才的LinearRef服务,取名为LRService。
客户端中,新建一个COperation类,实现业务操作。
- public class COperation
- {
- private LRService.LinearRefClient LR = null;
- private GraphicsLayer glayer = null;
- private bool isBusy = false;//indicate whether there is an operation is performing
- private Map Map1 = null;
- public COperation(Map map)
- {
- Map1=map;
- glayer = Map1.Layers["glayer"] as GraphicsLayer;
- //initialize the LR object
- LR = new RoadDycSeg.LRService.LinearRefClient();
- LR.IdentifyCompleted += new EventHandler<RoadDycSeg.LRService.IdentifyCompletedEventArgs>(LR_IdentifyCompleted);
- }
- private void LR_IdentifyCompleted(object sender, LRService.IdentifyCompletedEventArgs e)
- {
- LRService.IdentifyResult ir = e.Result;
- //add a point graphic to the map with attributes displaying by the maptip, maptip defined in mainpage.xaml
- if (ir.X > 0)//the result exists
- {
- Graphic g = new Graphic()
- {
- Geometry = new MapPoint(ir.X, ir.Y),
- Symbol = Application.Current.Resources["strobeSymbol"] as Symbol,
- };
- g.Attributes.Add("RouteID", ir.RouteID);
- g.Attributes.Add("桩号", ir.MValue);
- g.Attributes.Add("X坐标", ir.X);
- g.Attributes.Add("Y坐标", ir.Y);
- glayer.Graphics.Clear();
- glayer.Graphics.Add(g);
- }
- isBusy = false;
- }
-
- private void Map1_Identify(object sender, ESRI.ArcGIS.Client.Map.MouseEventArgs e)
- {
- if (!isBusy)
- {
- LR.IdentifyAsync(e.MapPoint.X, e.MapPoint.Y, Map1.Resolution);
- isBusy = true;
- }
- }
在需要使用Identify功能时,将Map控件的Click事件绑定到Map1_Identify即可。