http://www.gissky.net- GIS空间站

我要投稿 投稿指南 RSS订阅 网站资讯通告:
搜索: 您现在的位置: GIS空间站 >> 技术专栏 >> ArcGIS >> ArcGIS入门与应用 >> 正文

ArcGIS中的线性参考/动态分段技术

作者:diligent…    文章来源:ESRI    点击数:    更新时间:2009-12-15
摘要:Linear Referencing翻译过来是线性参考,在公路,管网等行业的GIS应用中时常提到。LR是一种利用沿着可测量的线要素的相对方位来存储地理位置的方法。


2、输入起始和终止桩号,要求将其间的路段显示在地图上:
要解决这个问题,关键是根据两个M值,求的其间一段公路的Geometry。利用的仍然是IRouteLocator2.Locate方法,其中的第一个参数routeLocation,传入的是用IRouteMeasureLineLocation接口定义的“线性位置”,具体可参考帮助文档。
仍然是在LinearRef.svc.cs文件的LinearRef类中,添加以下方法:

  1. [OperationContract(Name = "RetrieveRoutePortionGeometry")]
  2.         /// <summary>
  3.         /// used by the client,such as QueryPortion operation, to retrieve the geometry of the route portion
  4.         /// </summary>
  5.         /// <param name="routeid">the route id which the portion is part of</param>
  6.         /// <param name="fromMValue">fromMvalue</param>
  7.         /// <param name="toMValue">toMvalue</param>
  8.         /// <returns>a json string with specific format,such as [{"x":1111,"y":1111},{"x":2222,"y":2222},{"x":3333,"y":3333}.....]</returns>
  9.         public string RetrieveRoutePortionGeometry(string routeid, double fromMValue, double toMValue)
  10.         {
  11.             pServerContext = pSOM.CreateServerContext("shaanxi", "MapServer");
  12.             IRouteLocation routeLoc = pServerContext.CreateObject("esriLocation.RouteMeasureLineLocation") as IRouteLocation;
  13.             routeLoc.MeasureUnit = esriUnits.esriUnknownUnits;
  14.             routeLoc.RouteID = routeid;
  15.             routeLoc.LateralOffset = 0;
  16.             IRouteMeasureLineLocation rMLineLoc = (IRouteMeasureLineLocation)routeLoc;
  17.             rMLineLoc.FromMeasure = fromMValue;
  18.             rMLineLoc.ToMeasure = toMValue;

  19.             IGeometry geom;
  20.             esriLocatingError locError;
  21.             pRtLocator.Locate((IRouteLocation)rMLineLoc, out geom, out locError);

  22.             pServerContext.ReleaseContext();

  23.             //return the routeportion's vertices
  24.             StringBuilder sb = new StringBuilder("[");
  25.             IPolyline pLine = geom as IPolyline;
  26.             IPointCollection pPC = pLine as IPointCollection;
  27.             //json format such as:
  28.             //[{"x":1111,"y":1111},{"x":2222,"y":2222},{"x":3333,"y":3333}.....]
  29.             for (int i = 0; i < pPC.PointCount; i++)
  30.             {
  31.                 sb.Append("{\"x\":");
  32.                 sb.Append(pPC.get_Point(i).X.ToString());
  33.                 sb.Append(",\"y\":");
  34.                 sb.Append(pPC.get_Point(i).Y.ToString());
  35.                 sb.Append("},");
  36.             }
  37.             //remove the last ","
  38.             sb.Remove(sb.Length - 1, 1);
  39.             sb.Append("]");
  40.             return sb.ToString();
  41.         }

由于在服务器端的AO操作,产生的结果是ESRI.ArcGIS.Geometry.IGeometry类型,而客户端需要的则是ESRI.ArcGIS.Client.Geometry.Geometry类型,所以需要对结果序列化/反序列化,这里采用JSON字符串处理。客户端处理结果的函数仍然放在COperation.cs文件中,如下:

  1. private void LR_RetrieveRoutePortionGeometryCompleted(object sender,LRService.RetrieveRoutePortionGeometryCompletedEventArgs e)
  2.         {
  3.             //create a esri.arcgis.client.geometry.polyline from the coords of points contained in the json string
  4.             string jsonpoints = e.Result;
  5.             JsonArray jsonarrays = (JsonArray)JsonArray.Load(new System.IO.StringReader(jsonpoints));
  6.             ESRI.ArcGIS.Client.Geometry.Polyline line = new ESRI.ArcGIS.Client.Geometry.Polyline()
  7.             {
  8.                 SpatialReference = Map1.SpatialReference,
  9.             };
  10.             ESRI.ArcGIS.Client.Geometry.PointCollection pc = new ESRI.ArcGIS.Client.Geometry.PointCollection();
  11.             foreach (JsonObject jo in jsonarrays)
  12.             {
  13.                 pc.Add(new ESRI.ArcGIS.Client.Geometry.MapPoint(jo["x"], jo["y"]));
  14.             }
  15.             line.Paths.Add(pc);

  16.             //add a graphic
  17.             Graphic g = new Graphic()
  18.             {
  19.                 Geometry = line,
  20.                 Symbol = Application.Current.Resources["LineSymbol"] as SimpleLineSymbol,
  21.             };
  22.             glayer.Graphics.Add(g);
  23. }

Silverlight中提供了System.Json库,可以非常方便的对JSON字符串进行解析。

3、类似GoogleMap的交通流量地图:
首先看一下Google的交通流量地图:

可以看出,之所以能产生不同颜色段,需要的数据有3个:该段的起始和终止M值,该段的车流量。假设我们获取的业务数据表(EventTable)如下:

联想到上面第二个应用场景,我们便想可以获取所有交通数据后,用循环的办法展示出交通流量地图。但这里有两个问题需要思考:1、即使使用IRouteLocator2.LocateRow方法,服务器端根据起止M值解析出所有的分段图形需要一次循环,客户端将这些图形显示出来还需要一次循环,这样是否合理?2、由于结果显示在客户端,不同用户发出请求时,都将重复上面两个循环,如何改进?
解决问题一:在第二节的Linear Referencing实现原理中,提到了RouteEventSource类,可以将它看作是把Route FeatureClass和EventTable组合在一起的结果,而该类继承自FeatureClass,可当作一个FeatureClass来用。在ArcMap中观察生成后(利用Make Route Event Layer工具)的RouteEventSource层:

不仅包含了EventTable中的事件,还有了我们想要的Shape字段(可取出Geometry)。其实Shape字段中的内容,是利用Dynamic Segmentation技术动态计算出来的。看其图层属性便知,它是在内存中动态生成的:

有了FeatureClass,我们便可以对整个图层进行一次渲染,避免服务器端和客户端的两次循环了。这里要注意,FeatureClass是动态生成的,图层便是动态添加,在地图服务的REST接口中无法暴露出来,也就不能利用客户端API的渲染技术了。
解决问题二:利用SOA的思想,将交通流量图做成一个Traffic服务,不同用户请求时动态添加该服务即可。想想这个服务应该具有的特性:不同时段的交通图,需要动态生成;如果交通图已经生成,则无需重新生成,显示即可。所以我们采取池化方式服务(pooled-service)的特性,刚好解决这一问题。
这个场景我们用Web ADF来实现(客户端API与前两个场景相同)。
首先准备两个Map Service:底图服务:shaanxi,交通流量图服务:ShaanxiTraffic。
后者在ArcMap中打开是这样的:

ShaanxiTraffic服务除了动态生成的交通流量图外,不需要在显示任何信息,所以发布时是空白的;因为动态生成的RouteEventSource将作为图层插入到这个地图服务中,所以Route FeatureClass必须也在这个mxd中才行。
Default.aspx页面中放置一个MapResourceManager控件、一个Map控件、一个CallbackButton按钮和一个GridView控件。先看一下结果:

点击按钮后:

看一下Default.aspx.cs中按钮的点击事件:

  1. protected void CallbackButton1_Clicked(object sender, EventArgs args)
  2.     {
  3.         //先判断Traffic服务中是否已经产生结果
  4.         if (hasTrafficResult())//Traffic服务中已经有结果
  5.         {
  6.             //将结果插入地图
  7.             InsertTrafficService();
  8.         }
  9.         else//服务中还没有结果
  10.         {
  11.             //先生成交通流量结果
  12.             MakeResult();
  13.             //将结果插入地图
  14.             InsertTrafficService();
  15.         }
  16.         //显示RouteEventSource的属性表
  17.         DisplayAttributeTable();
  18.     }

思路都在代码中。依次来看几个函数:

  1. /// <summary>
  2.     /// 判断池化的Traffic服务中,获取的ServerContext是否已经产生结果
  3.     /// </summary>
  4.     /// <returns>true or false</returns>
  5.     private bool hasTrafficResult()
  6.     {
  7.         ESRI.ArcGIS.ADF.Identity identity = new ESRI.ArcGIS.ADF.Identity("ArcGISWebServices", "yourpassword", "");
  8.         ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection agsconn = new ESRI.ArcGIS.ADF.Connection.AGS.AGSServerConnection("machinename", identity);
  9.         agsconn.Connect();
  10.         pSOM = agsconn.ServerObjectManager;
  11.         ESRI.ArcGIS.Server.IServerContext pServerContext = pSOM.CreateServerContext("ShaanxiTraffic", "MapServer");
  12.         ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContext.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
  13.         ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
  14.         ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
  15.         pServerContext.ReleaseContext();
  16.         return pMap.LayerCount == 1 ? false : true;
  17.     }   

对于ShaanxiTraffic服务,在获得的Server Object中,查看当前Map的图层数(默认有一个隐藏的Road图层),若为1则还未产生结果。
下面是动态添加服务的函数,注意要设置背景透明,以便能够看到底图服务:

  1. /// <summary>
  2.     /// 动态添加服务
  3.     /// </summary>
  4.     private void InsertTrafficService()
  5.     {
  6.         if (MapResourceManager1.ResourceItems.Count==1)//若已插入交通流量服务,则不做动作
  7.         {
  8.             MapResourceItem mapResourceItem = new MapResourceItem();
  9.             GISResourceItemDefinition definition = new GISResourceItemDefinition();
  10.             mapResourceItem.Name = "Traffic";
  11.             definition.ResourceDefinition = "Layers@ShaanxiTraffic";
  12.             definition.DataSourceDefinition = "machinename";
  13.             definition.DataSourceType = "ArcGIS Server Local";
  14.             mapResourceItem.Parent = MapResourceManager1;
  15.             mapResourceItem.Definition = definition;
  16.             ESRI.ArcGIS.ADF.Web.DisplaySettings displaysettings = new ESRI.ArcGIS.ADF.Web.DisplaySettings();
  17.             displaysettings.Transparency = 0.0F;
  18.             displaysettings.Visible = true;
  19.             ESRI.ArcGIS.ADF.Web.ImageDescriptor imagedescriptor = new ESRI.ArcGIS.ADF.Web.ImageDescriptor();
  20.             imagedescriptor.TransparentBackground = true;
  21.             imagedescriptor.TransparentColor = System.Drawing.Color.White;
  22.             displaysettings.ImageDescriptor = imagedescriptor;
  23.             mapResourceItem.DisplaySettings = displaysettings;

  24.             MapResourceManager1.ResourceItems.Insert(0, mapResourceItem);
  25.             MapResourceManager1.CreateResource(mapResourceItem);

  26.             if (Map1.ImageBlendingMode == ImageBlendingMode.WebTier)
  27.             {
  28.                 Map1.Refresh();
  29.             }
  30.             else
  31.             {
  32.                 Map1.RefreshResource("Traffic");
  33.             }

  34.             CallbackButton1.CallbackResults.CopyFrom(Map1.CallbackResults);
  35.         }
  36.     }            
复制代码

MakeResult函数利用ESRI.ArcGIS.Location库来生成交通流量图。由于Traffic服务是池化服务,当第一个用户请求,生成交通流量图后,该实例(servercontext)会返回服务器上而不被销毁,所以下一个用户再次请求时可以直接显示。如果要实现实时的交通流量图,定时在服务器端重生servercontext,并读取当前时段的EventTable即可。

  1. /// <summary>
  2.     /// 生成交通流量的结果
  3.     /// </summary>
  4.     private void MakeResult()
  5.     {
  6.         ESRI.ArcGIS.Server.IServerContext pServerContextTraffic = pSOM.CreateServerContext("ShaanxiTraffic", "MapServer");
  7.         ESRI.ArcGIS.Carto.IMapServer2 pMapServer = pServerContextTraffic.ServerObject as ESRI.ArcGIS.Carto.IMapServer2;
  8.         ESRI.ArcGIS.Carto.IMapServerObjects2 pMapServerObjects = pMapServer as ESRI.ArcGIS.Carto.IMapServerObjects2;
  9.         ESRI.ArcGIS.Carto.IMap pMap = pMapServerObjects.get_Map(pMapServer.DefaultMapName);
  10.         //创建RouteLocator
  11.         IFeatureClass pFC = (GetLayerByName("Road",pMap) as IFeatureLayer).FeatureClass;
  12.         IDataset dS = (IDataset)pFC; // A polylineM feature class.
  13.         IName name = dS.FullName;
  14.         IRouteLocatorName rtLocatorName = pServerContextTraffic.CreateObject("esriLocation.RouteMeasureLocatorName") as IRouteLocatorName;
  15.     rtLocatorName.RouteFeatureClassName = name;
  16. rtLocatorName.RouteIDFieldName = "道路编码";
  17. rtLocatorName.RouteMeasureUnit = esriUnits.esriUnknownUnits;
  18.         name = (IName)rtLocatorName;
  19.         IRouteLocator2 pRtLocator = (IRouteLocator2)name.Open();

  20.         //创建RouteEventProperties,为RouteEventSource做准备
  21.         IRouteEventProperties2 rtProp = pServerContextTraffic.CreateObject("esriLocation.RouteMeasureLineProperties") as IRouteEventProperties2;
  22.         rtProp.AddErrorField = true;
  23.         rtProp.ErrorFieldName = "LOC_ERROR";
  24.         rtProp.EventMeasureUnit = esriUnits.esriUnknownUnits;
  25.         rtProp.EventRouteIDFieldName = "RouteID";
  26.         IRouteMeasureLineProperties rMLineProp = (IRouteMeasureLineProperties)rtProp;
  27.         rMLineProp.FromMeasureFieldName = "FromM";
  28.         rMLineProp.ToMeasureFieldName = "ToM";

  29.         //创建RouteEventSource
  30.         ITable eventTable = (pFC.FeatureDataset.Workspace as IFeatureWorkspace).OpenTable("lineEventsTable");
  31.         IDataset ds = (IDataset)eventTable;
  32.         IName name2 = ds.FullName;
  33.         IRouteEventSourceName rtEvtSrcName = pServerContextTraffic.CreateObject("esriLocation.RouteEventSourceName") as IRouteEventSourceName;
  34.         rtEvtSrcName.EventTableName = name2;
  35.         rtEvtSrcName.EventProperties = (IRouteEventProperties)rMLineProp;
  36.         rtEvtSrcName.RouteLocatorName = rtLocatorName;
  37.         name2 = (IName)rtEvtSrcName;
  38.         IRouteEventSource rtEvtSrc = (IRouteEventSource)name2.Open();
  39.         //转换到FeatureClass
  40.     pFC = rtEvtSrc as IFeatureClass;

  41. //将RouteEventSource添加到地图中,并进行唯一值渲染
  42.         IUniqueValueRenderer pUVR = pServerContextTraffic.CreateObject("esriCarto.UniqueValueRenderer") as IUniqueValueRenderer;
  43.         pUVR.FieldCount = 1;
  44.         pUVR.set_Field(0, "Vehicles");
  45.         pUVR.UseDefaultSymbol = false;
  46.         IFeatureCursor pFeatureCursor = pFC.Search(null, false);
  47.         IFeature pFeature = pFeatureCursor.NextFeature();
  48.         int ifiledindex = pFC.Fields.FindField("Vehicles");//EventTable中的车流量字段
  49.         while (pFeature != null)
  50.         {
  51.             ISimpleLineSymbol pSymbol = pServerContextTraffic.CreateObject("esriDisplay.SimpleLineSymbol") as ISimpleLineSymbol;
  52.             double value = double.Parse(pFeature.get_Value(ifiledindex).ToString());
  53.             if (value <= 20)
  54.                 pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(71, 196, 69)) as IColor;
  55.             else if (value > 20 && value <= 50)
  56.                 pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(255, 255, 0)) as IColor;
  57.             else if (value > 50 && value <= 100)
  58.                 pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(201, 59, 46)) as IColor;
  59.             else
  60.                 pSymbol.Color = ESRI.ArcGIS.ADF.Converter.ToRGBColor(pServerContextTraffic, System.Drawing.Color.FromArgb(250, 52, 17)) as IColor;
  61.             pSymbol.Width = 5;
  62.             pUVR.AddValue(pFeature.get_Value(ifiledindex).ToString(), "Vehicles", pSymbol as ISymbol);
  63.             pFeature = pFeatureCursor.NextFeature();
  64.         }
  65.         IFeatureLayer pFL = pServerContextTraffic.CreateObject("esriCarto.FeatureLayer") as IFeatureLayer;
  66.         pFL.Name = "RouteEventSource";
  67.         pFL.FeatureClass = pFC;
  68.         (pFL as IGeoFeatureLayer).Renderer = pUVR as IFeatureRenderer;
  69.         
  70.         pMap.AddLayer(pFL as ILayer);//to the 0 index
  71.         pMapServerObjects.RefreshServerObjects();
  72.         pServerContextTraffic.ReleaseContext();
  73.     }        

一点注意,在使用IServerContext接口时要及时进行释放。

上一页  [1] [2] [3] 

Tags:ArcGIS,线性参考  
责任编辑:gissky
关于我们 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 中国地图