ArcGIS API for Silverlight 使用GeometryService求解线与面的交点

最近在做项目的时候遇到一个问题,大致情况如下:
已知河流的面要素,需要根据用户输入的矩形以及设定的步长对河流进行网格划分,并得到网格与两边河岸的交点。
查了查资料,发现原生的ArcGIS API for Silverlight并没有提供实现该功能的借口,但是GeometryService提供了一个类似的功能:Intersect.
Intersect:其相交的情况有如下三种:
0
这里需要注意的时最后一个:线与线相交,从几何的角度来说,最后得到的结果应该是一个点,然而在GeometryService中,最后得到的结果是PolyLine,即是线,而且该线的Extent属性为null,也就是说线段长度为0.因此如果你想通过Intersect来求解两条线的交点实不可取的。那么这里的用处是什么呢?就是可以用来判断交点的个数。
从上图来看我们的情况是属于第二种:即面与线相交。
但是我们发现,这里得到的是相交的线,而我们要的是点,这个该怎么解决呢?
其实我们再一想,就可以发现,既然我们能够得到相交的线,那么我们是不是可以得到线的两个端点呢?如果可以得到两个端点是不是我们的问题就解决了呢?
答案只对了一半,因为我们再仔细一想,又会发现一个问题就是虽然我们可以得到两个端点,但是有的端点并不是网格与河流面要素的交点,例如下图所示:
0
上图表示对河流进行划分的网格
如果按照上面的思路我们将得到如下的结果:
0
我们发现,虽然得到了与河流的交点但是左右两侧水平线的端点也包含了进来,而显然它们都不是与河流的交点,那么这里该怎么去除呢?
从上图可以知道,左右边界上非河岸的交点都位于网格的边缘,假设我们设网格矩形的区域为(Xmin,Ymin,Xmax,Ymax),则这些点的X坐标都为Xmin或者Xmax,所以我们由此可以去除边界上的点,然而这里有一个问题是:
当我们按照一定的步长划分时,不一定会整分,即出现如下的情况:
0
我们发现上面和右边并不是刚好等分的,上面和右面部分线条超出了“范围”,虽然这并不影响河流的交点的提取,但是多少显得不那么好看。
下面我们先看看构造网格的代码:
0
 public void CreateGrid(double xMin,double xMax,double yMin,double yMax,double xstep,double ystep)         {             //定义两个Point,确定一条直线             MapPoint mp1;             MapPoint mp2;             //由步长确定划分成多少份             int Nx = Convert.ToInt32((xMax - xMin)/xstep);             int Ny = Convert.ToInt32((yMax - yMin) / ystep);             //构造竖直方向的线             for (int i = 0; i <nx+1; i++)             {=""                 mp1="CreateMapPoint(xMin+i*xstep, yMin);"                 mp2="CreateMapPoint(xMin + i * xstep,yMax);"                 createline(mp1, mp2);=""             }=""             ="" 构造水平方向的线=""             for (int i =" 0; i < Ny+1; i++)"                 mp1 =" CreateMapPoint(xMin, yMin +i * ystep);"                 mp2 =" CreateMapPoint(xMax, yMin + i * ystep);" rectextent为自定义的一种结构体,存储每一次网格划分时的矩形范围=""             recextent =" new RectExtent()"                 xmin =" Convert.ToDouble((xMin).ToString("#0.000")),"                 xmax =" Convert.ToDouble((xMin + Nx * xstep).ToString("#0.000")),"                 ymin =" Convert.ToDouble((yMin).ToString("#0.000")),"                 ymax =" Convert.ToDouble((yMin + Ny * ystep).ToString("#0.000"))"             };=""         }<="" span="">
0
以上的CreateMapPoint为自定义方法,表示根据X,Y坐标,新建一个点(MapPoint),示例代码如下:
 public MapPoint CreateMapPoint(double x,double y)         {             return new MapPoint(x, y);         }
CreateLine表示根据得到的两个点,绘制一条直线:
0
 public void CreateLine(MapPoint p1,MapPoint p2)         {             ESRI.ArcGIS.Client.Geometry.Polyline polyline = new ESRI.ArcGIS.Client.Geometry.Polyline();             ESRI.ArcGIS.Client.Geometry.PointCollection pc=new ESRI.ArcGIS.Client.Geometry.PointCollection ();             pc.Add(p1);             pc.Add(p2);             polyline.Paths.Add(pc);             //记得对空间坐标系赋值,否则Simplify会出错。             polyline.SpatialReference = map1.SpatialReference;             AddLineGraphic(polyline);                      }
0
AddLineGraphic表示将生成的线添加到图层中:
0
 public void AddLineGraphic(ESRI.ArcGIS.Client.Geometry.Polyline polyline)         {             Graphic g = new Graphic()             {                 Geometry = polyline,                 Symbol = LayoutRoot.Resources["LineSymbol"] as SimpleLineSymbol             };                        glayer.Graphics.Add(g);         }
0
同理,之后的AddPointGraphic即向图层中添加点:
0
 public void AddPointGraphic(ESRI.ArcGIS.Client.Geometry.MapPoint point)         {             Graphic g = new Graphic()             {                 Geometry = point,                 Symbol = LayoutRoot.Resources["PointSymbol"] as SimpleMarkerSymbol,             };             glayer.Graphics.Add(g);         }
0
这里我们实现了网格的绘制,但是你会发现得到的网格如本文第3张图片所示。
这并不是我们所希望的,这里我们修改一下构造水平和垂直直线的代码,如下所示:
0
0
  //构造竖直方向的线             for (int i = 0; i <nx+1; i++)             {=""                 mp1="CreateMapPoint(xMin+i*xstep, yMin);"                 ="" mp2="CreateMapPoint(xMin + i * xstep,yMax);"                 mp2 =" CreateMapPoint(xMin + i * xstep, yMin + Ny * ystep);"                 createline(mp1, mp2);=""             }=""             ="" 构造水平方向的线=""             for (int i =" 0; i < Ny+1; i++)"                 mp1 =" CreateMapPoint(xMin, yMin +i * ystep);" mp2 =" CreateMapPoint(xMax, yMin + i * ystep);"             }<="" span="">
0
这里我们将Xmax改成了Xmax+Nx*Xstep,同理其他。
0
这样我们就能得到闭合的网格了。
但是我们还是没有说如何解决上面说的那个问题,上面我们说到通过Xmin和Xmax来判断是否是河流的交点,但是实际运行程序时,是无法实现的,为什么呢?
因为我们再使用Geometry的Intersect时,再Intersect的过程中对每一个点(交线的端点)的坐标值都会进行相应的简化(四舍五入),这样我们在Intersect完成事件函数中获得端点值就已经不等于Intersect之前的了,因此也就无法根据线段端点的Xmin和Xmax值是否等于边界值来取舍交点。
具体过程可以看下面的示意图:
0
因此,通过Xmin和Xmax的方式也不行。那到底该怎么办呢?
这里我们仔细一想就会发现,Intersect之后的Xmin和Xmax和实际上的X范围很相近,因为Intersect实际上就是对X范围进行了四舍五入。
而一般来说Intersect之后的的X范围和初始时的X范围相差不超过1.甚至是0.1。于是我们将Intersect之后的值与初始时的值做作差取绝对值,这样就可以确定Intersect之后,线段的端点是不是交点了。
0
下面给出GeometryService的Intersect完成事件处理函数的代码:
0
0
0
   void geometryService_IntersectCompleted(object sender, GraphicsEventArgs e)         {             glayer.Graphics.Clear();             foreach (Graphic g in e.Results)             {                 g.Symbol = LayoutRoot.Resources["ResultsLineSymbol"] as SimpleLineSymbol;                 if (g.Geometry.Extent != null)                 {                     #region 垂直线                     if (g.Geometry.Extent.Width == 0.0)//垂直线两个端点即为交点                     {                        foreach (ESRI.ArcGIS.Client.Geometry.PointCollection pc in (g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline).Paths)                         {                             foreach (MapPoint mp in pc)                             {                                 if (Math.Abs(mp.Y - recExtent.Ymin) < 0.1 ||                                     Math.Abs(mp.Y - recExtent.Ymax) < 0.1)                                 {                                     continue;                                 }                                 else                                 {                                     AddPointGraphic(mp);                                 }                             }                         }                     }                     #endregion                     #region 水平线                     if (g.Geometry.Extent.Height == 0.0)                     {                         foreach (ESRI.ArcGIS.Client.Geometry.PointCollection pc in (g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline).Paths)                         {                             foreach (MapPoint mp in pc)                             {                                 if (Math.Abs(mp.X - recExtent.Xmin) < 0.1 ||                                     Math.Abs(mp.X - recExtent.Xmax) < 0.1)                                 {                                     continue;                                 }                                 else                                 {                                     AddPointGraphic(mp);                                 }                             }                         }                     }                     #endregion                 }                 glayer.Graphics.Add(g);             }         }
0
这里我们将边界点的值与交线的端点值比较,如果两者之差小于0.1,则表示该点为边界点,舍去
0
0
以上解释了实现过程中的关键部分,接下来看看具体实现的流程:
0
(1):绘制矩形,网格划分
自定义结构体:
 public struct RectExtent     {        public double Xmin;        public double Ymin;        public double Xmax;        public double Ymax;     }
声明如下变量:
0
       //定义绘制矩形的画笔           private Draw drawRect = null;         //定义集合服务变量         private GeometryService geometryService = null;         //用来承载绘制的线和点         GraphicsLayer glayer = null;         //河流要素         FeatureLayer fpolygonlayer = null;         //自定义结构体变量         private RectExtent recExtent;
0
在构造函数中实例化:
0
   public MainPage()         {             InitializeComponent();             geometryService = new GeometryService("http://qzj-pc/ArcGIS/rest/services/Geometry/GeometryServer");             geometryService.IntersectCompleted += new EventHandler(geometryService_IntersectCompleted);             geometryService.SimplifyCompleted += new EventHandler(geometryService_SimplifyCompleted);                          geometryService.Failed += new EventHandler(geometryService_Failed);             glayer=map1.Layers["LineLayer"] as GraphicsLayer;             fpolygonlayer = map1.Layers["RiverPolygon"] as FeatureLayer;             drawGridButton.Click += new RoutedEventHandler(drawGridButton_Click);             intersectButton.Click += new RoutedEventHandler(intersectButton_Click);             ClearButton.Click += new RoutedEventHandler(ClearButton_Click);             drawRect = new Draw(map1);             drawRect.DrawMode = DrawMode.Rectangle;             drawRect.DrawComplete += new EventHandler(drawRect_DrawComplete);             drawRect.IsEnabled = false;         }
0
点击绘制要输Button,绘制网格
0
 void drawGridButton_Click(object sender, RoutedEventArgs e)         {             //确定用户输入了步长信息             if (xStepTextBox.Text.Trim() == "" || yStepTextBox.Text.Trim() == "")             {                 MessageBox.Show("请输入步长信息");                 return;             }             drawRect.IsEnabled = true;         }
0
 void drawRect_DrawComplete(object sender, DrawEventArgs e)         {             drawRect.IsEnabled = false;                        CreateGrid(e.Geometry.Extent.XMin, e.Geometry.Extent.XMax, e.Geometry.Extent.YMin, e.Geometry.Extent.YMax, Convert.ToDouble(xStepTextBox.Text), Convert.ToDouble(yStepTextBox.Text));         }
CreateGrid在上面已经给出,在此不再说明。
(2)Silmplify河流要素
 void intersectButton_Click(object sender, RoutedEventArgs e)         {             geometryService.SimplifyAsync(fpolygonlayer.Graphics);         }
(3)在Simplify事件完成函数中进行Intersect操作。
由于这里使用的河流要素只有一个图层所以生成的结果Results只有一个要素,即索引值为0.
  void geometryService_SimplifyCompleted(object sender, GraphicsEventArgs e)         {             geometryService.IntersectAsync(glayer.Graphics, e.Results[0].Geometry);         }
(4).在Intersect事件完成函数中筛选正确的交点,代码上面已经给出,在此不在说明。
(5)最后将正确的点添加到图层中。
最后便可以得到正确的结果:
0
思考:如何绘制出网格的交点呢?
预告:下一篇将讲解线与线交点的求解,欢迎继续关注!
 (版权所有,转载请标明出处)
绿色通道:  好文要顶  关注我  收藏该文 与我联系 
在前一篇的博文中说到了线与面交点的求解,其中使用的方法是利用GeometryService的Simplify+Intersect服务,通过获得线与面的交点,间接的通过交线的端点得到线与面的交点。过程如下所示:
0
然而在上文中我们提到过一句,Geometry中的Intersect无法得到线与线的交点,尽管当我们使用Intersect求线与线相交时,可以返回相交的结果,但是无法获取交点,因为Intersect求线与线交点返回的是一个Extent为null的线要素,我们无法将其用点在地图上表达出来。
那么这里我们该怎么做呢?
这里我们用到了Geometry的另一个服务:TrimExtend(修剪扩展)
具体示例可参考:
关于TrimExtend功能的效果图:
0
我们发现,使用TrimExtend之后网格水平线向右方向进行了衍生,并与线要素相交了,而位于河流下方的网格则被剪切掉了。
关于裁剪和延伸的方向(比如上图是向右延伸,还可以设置向左延伸,或者双向延伸等),可以通过CurveExtension属性来设置,关于CurveExtension的属性说明参看官方文档:
需要说明的是,TrimExtend返回的结果是线要素,而返回线要素的一端就是线要素与线要素的交点(仅当线要素相交时,从上图我们会发现没有相交的要素也被返回了,因此这里还需要进一步的筛选,具体将在后面说明)
通过以上分析,我们知道只要获取返回线要素的端点就可以得到相交的结果。
下面是具体步骤:
1.构建网格(这里也可以使用某一图层的线要素,本文采用自己构建网格,求网格与线要素相交的节点)
关键代码:
a.根据两个点构造一条直线
0
   public void CreateLine(MapPoint p1,MapPoint p2)         {             ESRI.ArcGIS.Client.Geometry.Polyline polyline = new ESRI.ArcGIS.Client.Geometry.Polyline();             ESRI.ArcGIS.Client.Geometry.PointCollection pc=new ESRI.ArcGIS.Client.Geometry.PointCollection ();             pc.Add(p1);             pc.Add(p2);             polyline.Paths.Add(pc);             //记得对空间坐标系赋值,否则Simplify会出错。             polyline.SpatialReference = map1.SpatialReference;             AddLineGraphic(polyline);           }
0
AddLineGraphic为自定义方法,作用是将线要素添加到图层中,示例代码如下:
0
  public void AddLineGraphic(ESRI.ArcGIS.Client.Geometry.Polyline polyline)         {             Graphic g = new Graphic()             {                 Geometry = polyline,                 Symbol = LayoutRoot.Resources["LineSymbol"] as SimpleLineSymbol             };                        glayer.Graphics.Add(g);         }
0
这里用户可以自定义网格的步长,根据绘制的矩形或多边形范围构建网格。
0
   public void CreateGrid(double xMin,double xMax,double yMin,double yMax,double xstep,double ystep)         {             //定义两个Point,确定一条直线             MapPoint mp1;             MapPoint mp2;             //由步长确定划分成多少份             int Nx = Convert.ToInt32((xMax - xMin)/xstep);             int Ny = Convert.ToInt32((yMax - yMin) / ystep);             //构造竖直方向的线             for (int i = 0; i <nx+1; i++)             {=""                 mp1="CreateMapPoint(xMin+i*xstep, yMin);"                 ="" mp2="CreateMapPoint(xMin + i * xstep,yMax);"                 mp2 =" CreateMapPoint(xMin + i * xstep, yMin + Ny * ystep);"                 createline(mp1, mp2);=""             }=""             ="" 构造水平方向的线=""             for (int i =" 0; i < Ny+1; i++)"                 mp1 =" CreateMapPoint(xMin, yMin +i * ystep);" mp2 =" CreateMapPoint(xMax, yMin + i * ystep);" rectextent为自定义的一种结构体,存储每一次网格划分时的矩形范围=""             recextent =" new RectExtent()"                 xmin =" Convert.ToDouble((xMin).ToString("#0.000")),"                 xmax =" Convert.ToDouble((xMin + Nx * xstep).ToString("#0.000")),"                 ymin =" Convert.ToDouble((yMin).ToString("#0.000")),"                 ymax =" Convert.ToDouble((yMin + Ny * ystep).ToString("#0.000"))"             };=""         }=""      <="" span="">
0
效果示意图:
0
下面就通过TrimExtend服务来实现获取交点的功能
2.调用TrimExtend功能
0
private void intersectCenterLineButton_Click(object sender, RoutedEventArgs e)         {             //存储线要素的集合             List polyLineListGrid = new List();             List polyLineListriver = new List();             if (glayer.Graphics.Count == 0 || flayer.Graphics.Count == 0)             {                 MessageBox.Show("请确认输入的线要素不为空");                 return;             }             //遍历图层的线要素             foreach (Graphic g in glayer.Graphics)             {                 polyLineListGrid.Add(g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline);             }                          foreach (Graphic g in flayer.Graphics)             {                 polyLineListriver.Add(g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline);             }             /*************************************              * 被相交的图层(在本文中只有一个要素,所以索引为0)              * 第一个参数表示相交的要素,例如网格,因此是一个集合              * 第二个参数表示被相交的要素,例如一条河流,因此是一个线要素,而不是要素的集合              * 第三个参数表示延伸(剪切)的属性              *************************************/             geometryService.TrimExtendAsync(polyLineListGrid, polyLineListriver[0], CurveExtension.NoExtendAtFrom);         }
0
 接下来就是对结果进行处理,上面我们说过,所有交线的一个端点即为直线的交点。下图是返回直线的结果(红色线段即为结果),这里需要注意的是
使用TrimExtend功能时,CurveExtension属性设置为NoExtendAtFrom,这样网格就会从起点向右寻找格线的交点,没有交点则向右延长,直到相交。
0
(1)交线的长度一定小于初始时绘制的网格的范围,即
   水平方向的交线长度<|Xmax-Xmin|, Xmax,Xmin为网格X轴的最大与最小值。
   垂直方向的交线长度<|Ymax-Ymin|, Ymax,Ymin为网格Y轴的最大与最小值。
(2)针对于水平和垂直网格,交点的坐标始终是最小值(当是斜线的时候,可通过获取交线的斜率,然后再求得交点)
知道了这两点,我们实现起来就I叫容易了。下面是获取结果的代码:
0
    void geometryService_TrimExtendCompleted(object sender, GraphicsEventArgs e)         {             glayer.Graphics.Clear();             MapPoint intersectPoint = null;             foreach (Graphic g in e.Results)             {                 g.Symbol = LayoutRoot.Resources["ResultsLineSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol;                 glayer.Graphics.Add(g);                 if (g.Geometry.Extent != null)                 {                     /*recExtent结构体存储了绘制矩形的范围                      * 这里通过比较返回结果中线段长度来判断其是不是交线                      * 同时本文绘制的网格是垂直和水平的,                      * 所以交线的长度肯定小于绘制矩形的范围(这里只需要判断水平方向即可)                      * 关于斜线的情况,判断方法类似                      */                     if ((g.Geometry.Extent.XMax - g.Geometry.Extent.XMin) < (recExtent.Xmax - recExtent.Xmin))                     {                         intersectPoint = CreateMapPoint(g.Geometry.Extent.XMin, g.Geometry.Extent.YMin);                         AddPointGraphic(intersectPoint);                     }                 }             }         }     
0
其中CreateMapPoint方法表示通过X,Y坐标构造一个点,AddPointGraphic表示将点添加到地图中显示。
0
 //由X,Y构造一个点 public MapPoint CreateMapPoint(double x,double y)         {             return new MapPoint(x, y);         } //将一个点添加到Layer中  public void AddPointGraphic(ESRI.ArcGIS.Client.Geometry.MapPoint point)         {             Graphic g = new Graphic()             {                 Geometry = point,                 Symbol = LayoutRoot.Resources["PointSymbol"] as SimpleMarkerSymbol,             };             glayer.Graphics.Add(g);         }
0
这样我们就求得了线与线的交点。
示例图:
a.构造的网格线
0
 
 b.交点
0
总结:
本文使用TrimExtend来求解交点,过程是:获取TrimExtend功能返回的线段,筛选出交线,然后获得交线的端点,即为交点。本文讲解了网格是水平和垂直时相交的情况,当线段是不规则的情况下,可以通过交线的斜率来确定交点。
 下一篇,将讲解构造斜线网格来求交点,欢迎继续关注。
(版权所有,转载请标明出处)
 
先来看看最终的效果图吧!
0
对于岛屿情况:
0
相信很多人都做过关于河流的分析,而其中多少会涉及到河流的网格划分。二这也一直是一个难点,尤其是在Web端(本文只针对Silverlight)。而就目前查阅的资料来看,关于这方面的资料少之又少。大部分的网格划分要不就是理论,要不就是基于桌面的,而桌面的网格划分要比Web容易的多,此外,还有一种情况就是先在ArcMap中划分好,在Web端查询一定区域的网格划分点。
Web端的河流网格划分有以下几个难点:
(1).如何对河流进行网格划分得到网格点(网格划分点落在河流之内)
(2).得到的网格点如何与Web地图结合,建立地理关系
(3).针对不同的河流复杂程度(例如:河流弯曲程度不一,包含岛屿等),网格划分方法同样适用
解决了以上3个问题,那么河流网格划分问题就基本可以解决了。
 
下面我们来看看如何解决上面的3个问题。
 
目前在多数的网格划分采用的是贴体网格划分,通过求解微分方程,将不规则的河流转化到X-Y坐标系下,从而便于求解,这种方式得到的网格点很好,然而繁琐,特别是在在Web端实现更加困难,同时也无法直接与地图结合。此外偏微分方程的求解还需要一定的数学功底,因此并不适合我们普通的开发人员。
在这里我们采取添加控制点——绘制多边形——等分多边形——求交线——等分交线的方式来得到最后的网格。请看下面的示意图:
a.添加控制点,得到多边形
0
0
我们绘制了一个沿着河流的多边形,接下来,我们按照设置的等分数,等分多边形,如下图所示:
0
这里实现的原理的,得到河流两边的控制点,根据各边的控制点,得到各边的总长度,然后根据总长度按照设置的等分数,等分两边的多边形。接着连接两边的等分点。接下来我们就需要求解这些等分线与河流的交线。如下图所示:
0
这样得到与河流的交线之后,我们再对每一段的交线进行等距划分,然后得到等分点,最后如下图所示:
0
这样我们便得到了最终的网格点。
以上过程不需要借助任何第三方控件,只需要使用ArcGIS API for Silverlight,结合GeometryService即可完成,代码量不多,实现起来也不难。
在此提供这么一种方法大家可以自己试一试。具体代码在后续的工作中将给出。
总结:
本文方法方法的优点:
1.适用于弯曲程度不一的河流,包括河流包含岛屿等情况
2.可以按照指定的点的个数,疏密情况进行网格划分。
3.可以很好的结合Web地图
该方法的缺点:
1.目前还无法在河流交叉地方进行网格划分。
2.网格线不能保证与河流的中心线垂直。
3.多边形控制点直接影响着网格的质量。
 
在前一篇的博文中说到了线与面交点的求解,其中使用的方法是利用GeometryService的Simplify+Intersect服务,通过获得线与面的交点,间接的通过交线的端点得到线与面的交点。过程如下所示:
0
然而在上文中我们提到过一句,Geometry中的Intersect无法得到线与线的交点,尽管当我们使用Intersect求线与线相交时,可以返回相交的结果,但是无法获取交点,因为Intersect求线与线交点返回的是一个Extent为null的线要素,我们无法将其用点在地图上表达出来。
那么这里我们该怎么做呢?
这里我们用到了Geometry的另一个服务:TrimExtend(修剪扩展)
具体示例可参考:
关于TrimExtend功能的效果图:
0
我们发现,使用TrimExtend之后网格水平线向右方向进行了衍生,并与线要素相交了,而位于河流下方的网格则被剪切掉了。
关于裁剪和延伸的方向(比如上图是向右延伸,还可以设置向左延伸,或者双向延伸等),可以通过CurveExtension属性来设置,关于CurveExtension的属性说明参看官方文档:
需要说明的是,TrimExtend返回的结果是线要素,而返回线要素的一端就是线要素与线要素的交点(仅当线要素相交时,从上图我们会发现没有相交的要素也被返回了,因此这里还需要进一步的筛选,具体将在后面说明)
通过以上分析,我们知道只要获取返回线要素的端点就可以得到相交的结果。
下面是具体步骤:
1.构建网格(这里也可以使用某一图层的线要素,本文采用自己构建网格,求网格与线要素相交的节点)
关键代码:
a.根据两个点构造一条直线
0
public void CreateLine(MapPoint p1,MapPoint p2) { ESRI.ArcGIS.Client.Geometry.Polyline polyline = new ESRI.ArcGIS.Client.Geometry.Polyline(); ESRI.ArcGIS.Client.Geometry.PointCollection pc=new ESRI.ArcGIS.Client.Geometry.PointCollection (); pc.Add(p1); pc.Add(p2); polyline.Paths.Add(pc); //记得对空间坐标系赋值,否则Simplify会出错。 polyline.SpatialReference = map1.SpatialReference; AddLineGraphic(polyline); }
0
AddLineGraphic为自定义方法,作用是将线要素添加到图层中,示例代码如下:
0
public void AddLineGraphic(ESRI.ArcGIS.Client.Geometry.Polyline polyline) { Graphic g = new Graphic() { Geometry = polyline, Symbol = LayoutRoot.Resources["LineSymbol"] as SimpleLineSymbol }; glayer.Graphics.Add(g); }
0
这里用户可以自定义网格的步长,根据绘制的矩形或多边形范围构建网格。
0
public void CreateGrid(double xMin,double xMax,double yMin,double yMax,double xstep,double ystep) { //定义两个Point,确定一条直线 MapPoint mp1; MapPoint mp2; //由步长确定划分成多少份 int Nx = Convert.ToInt32((xMax - xMin)/xstep); int Ny = Convert.ToInt32((yMax - yMin) / ystep); //构造竖直方向的线 for (int i = 0; i <nx+1; i++)="" {="" mp1="CreateMapPoint(xMin+i*xstep," ymin);="" mp2="CreateMapPoint(xMin" +="" i="" *="" xstep,ymax);="" xstep,="" ymin="" ny="" ystep);="" createline(mp1,="" mp2);="" }="" 构造水平方向的线="" for="" (int="" <="" ny+1;="" +i="" nx="" rectextent为自定义的一种结构体,存储每一次网格划分时的矩形范围="" recextent="new" rectextent()="" xmin="Convert.ToDouble((xMin).ToString("#0.000"))," xmax="Convert.ToDouble((xMin" xstep).tostring("#0.000")),="" ymax="Convert.ToDouble((yMin" ystep).tostring("#0.000"))="" };="" span="">
0
效果示意图:
0
下面就通过TrimExtend服务来实现获取交点的功能
2.调用TrimExtend功能
0
private void intersectCenterLineButton_Click(object sender, RoutedEventArgs e) { //存储线要素的集合 List polyLineListGrid = new List(); List polyLineListriver = new List(); if (glayer.Graphics.Count == 0 || flayer.Graphics.Count == 0) { MessageBox.Show("请确认输入的线要素不为空"); return; } //遍历图层的线要素 foreach (Graphic g in glayer.Graphics) { polyLineListGrid.Add(g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline); } foreach (Graphic g in flayer.Graphics) { polyLineListriver.Add(g.Geometry as ESRI.ArcGIS.Client.Geometry.Polyline); } /************************************* * 被相交的图层(在本文中只有一个要素,所以索引为0) * 第一个参数表示相交的要素,例如网格,因此是一个集合 * 第二个参数表示被相交的要素,例如一条河流,因此是一个线要素,而不是要素的集合 * 第三个参数表示延伸(剪切)的属性 *************************************/ geometryService.TrimExtendAsync(polyLineListGrid, polyLineListriver[0], CurveExtension.NoExtendAtFrom); }
0
 接下来就是对结果进行处理,上面我们说过,所有交线的一个端点即为直线的交点。下图是返回直线的结果(红色线段即为结果),这里需要注意的是
使用TrimExtend功能时,CurveExtension属性设置为NoExtendAtFrom,这样网格就会从起点向右寻找格线的交点,没有交点则向右延长,直到相交。
0
(1)交线的长度一定小于初始时绘制的网格的范围,即
   水平方向的交线长度<|Xmax-Xmin|, Xmax,Xmin为网格X轴的最大与最小值。
   垂直方向的交线长度<|Ymax-Ymin|, Ymax,Ymin为网格Y轴的最大与最小值。
(2)针对于水平和垂直网格,交点的坐标始终是最小值(当是斜线的时候,可通过获取交线的斜率,然后再求得交点)
知道了这两点,我们实现起来就I叫容易了。下面是获取结果的代码:
0
void geometryService_TrimExtendCompleted(object sender, GraphicsEventArgs e) { glayer.Graphics.Clear(); MapPoint intersectPoint = null; foreach (Graphic g in e.Results) { g.Symbol = LayoutRoot.Resources["ResultsLineSymbol"] as ESRI.ArcGIS.Client.Symbols.Symbol; glayer.Graphics.Add(g); if (g.Geometry.Extent != null) { /*recExtent结构体存储了绘制矩形的范围 * 这里通过比较返回结果中线段长度来判断其是不是交线 * 同时本文绘制的网格是垂直和水平的, * 所以交线的长度肯定小于绘制矩形的范围(这里只需要判断水平方向即可) * 关于斜线的情况,判断方法类似 */ if ((g.Geometry.Extent.XMax - g.Geometry.Extent.XMin) < (recExtent.Xmax - recExtent.Xmin)) { intersectPoint = CreateMapPoint(g.Geometry.Extent.XMin, g.Geometry.Extent.YMin); AddPointGraphic(intersectPoint); } } } }
0
其中CreateMapPoint方法表示通过X,Y坐标构造一个点,AddPointGraphic表示将点添加到地图中显示。
0
//由X,Y构造一个点 public MapPoint CreateMapPoint(double x,double y) { return new MapPoint(x, y); } //将一个点添加到Layer中 public void AddPointGraphic(ESRI.ArcGIS.Client.Geometry.MapPoint point) { Graphic g = new Graphic() { Geometry = point, Symbol = LayoutRoot.Resources["PointSymbol"] as SimpleMarkerSymbol, }; glayer.Graphics.Add(g); }
0
这样我们就求得了线与线的交点。
示例图:
a.构造的网格线
0
 
 b.交点
0
总结:
本文使用TrimExtend来求解交点,过程是:获取TrimExtend功能返回的线段,筛选出交线,然后获得交线的端点,即为交点。本文讲解了网格是水平和垂直时相交的情况,当线段是不规则的情况下,可以通过交线的斜率来确定交点。
 
posted @ 2022-08-03 10:02  devgis  阅读(374)  评论(0编辑  收藏  举报