ArcGIS API for Silverlight 使用GeometryService求解线与面的交点
最近在做项目的时候遇到一个问题,大致情况如下:
已知河流的面要素,需要根据用户输入的矩形以及设定的步长对河流进行网格划分,并得到网格与两边河岸的交点。
查了查资料,发现原生的ArcGIS API for Silverlight并没有提供实现该功能的借口,但是GeometryService提供了一个类似的功能:Intersect.
Intersect:其相交的情况有如下三种:
这里需要注意的时最后一个:线与线相交,从几何的角度来说,最后得到的结果应该是一个点,然而在GeometryService中,最后得到的结果是PolyLine,即是线,而且该线的Extent属性为null,也就是说线段长度为0.因此如果你想通过Intersect来求解两条线的交点实不可取的。那么这里的用处是什么呢?就是可以用来判断交点的个数。
从上图来看我们的情况是属于第二种:即面与线相交。
但是我们发现,这里得到的是相交的线,而我们要的是点,这个该怎么解决呢?
其实我们再一想,就可以发现,既然我们能够得到相交的线,那么我们是不是可以得到线的两个端点呢?如果可以得到两个端点是不是我们的问题就解决了呢?
答案只对了一半,因为我们再仔细一想,又会发现一个问题就是虽然我们可以得到两个端点,但是有的端点并不是网格与河流面要素的交点,例如下图所示:
上图表示对河流进行划分的网格
如果按照上面的思路我们将得到如下的结果:
我们发现,虽然得到了与河流的交点但是左右两侧水平线的端点也包含了进来,而显然它们都不是与河流的交点,那么这里该怎么去除呢?
从上图可以知道,左右边界上非河岸的交点都位于网格的边缘,假设我们设网格矩形的区域为(Xmin,Ymin,Xmax,Ymax),则这些点的X坐标都为Xmin或者Xmax,所以我们由此可以去除边界上的点,然而这里有一个问题是:
当我们按照一定的步长划分时,不一定会整分,即出现如下的情况:
我们发现上面和右边并不是刚好等分的,上面和右面部分线条超出了“范围”,虽然这并不影响河流的交点的提取,但是多少显得不那么好看。
下面我们先看看构造网格的代码:
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="">
以上的CreateMapPoint为自定义方法,表示根据X,Y坐标,新建一个点(MapPoint),示例代码如下:
public MapPoint CreateMapPoint(double x,double y) { return new MapPoint(x, y); }
CreateLine表示根据得到的两个点,绘制一条直线:
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); }
AddLineGraphic表示将生成的线添加到图层中:
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); }
同理,之后的AddPointGraphic即向图层中添加点:
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); }
这里我们实现了网格的绘制,但是你会发现得到的网格如本文第3张图片所示。
这并不是我们所希望的,这里我们修改一下构造水平和垂直直线的代码,如下所示:
//构造竖直方向的线 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="">
这里我们将Xmax改成了Xmax+Nx*Xstep,同理其他。
这样我们就能得到闭合的网格了。
但是我们还是没有说如何解决上面说的那个问题,上面我们说到通过Xmin和Xmax来判断是否是河流的交点,但是实际运行程序时,是无法实现的,为什么呢?
因为我们再使用Geometry的Intersect时,再Intersect的过程中对每一个点(交线的端点)的坐标值都会进行相应的简化(四舍五入),这样我们在Intersect完成事件函数中获得端点值就已经不等于Intersect之前的了,因此也就无法根据线段端点的Xmin和Xmax值是否等于边界值来取舍交点。
具体过程可以看下面的示意图:
因此,通过Xmin和Xmax的方式也不行。那到底该怎么办呢?
这里我们仔细一想就会发现,Intersect之后的Xmin和Xmax和实际上的X范围很相近,因为Intersect实际上就是对X范围进行了四舍五入。
而一般来说Intersect之后的的X范围和初始时的X范围相差不超过1.甚至是0.1。于是我们将Intersect之后的值与初始时的值做作差取绝对值,这样就可以确定Intersect之后,线段的端点是不是交点了。
下面给出GeometryService的Intersect完成事件处理函数的代码:
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.1,则表示该点为边界点,舍去
以上解释了实现过程中的关键部分,接下来看看具体实现的流程:
(1):绘制矩形,网格划分
自定义结构体:
public struct RectExtent { public double Xmin; public double Ymin; public double Xmax; public double Ymax; }
声明如下变量:
//定义绘制矩形的画笔 private Draw drawRect = null; //定义集合服务变量 private GeometryService geometryService = null; //用来承载绘制的线和点 GraphicsLayer glayer = null; //河流要素 FeatureLayer fpolygonlayer = null; //自定义结构体变量 private RectExtent recExtent;
在构造函数中实例化:
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; }
点击绘制要输Button,绘制网格
void drawGridButton_Click(object sender, RoutedEventArgs e) { //确定用户输入了步长信息 if (xStepTextBox.Text.Trim() == "" || yStepTextBox.Text.Trim() == "") { MessageBox.Show("请输入步长信息"); return; } drawRect.IsEnabled = true; }
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)最后将正确的点添加到图层中。
最后便可以得到正确的结果:
思考:如何绘制出网格的交点呢?
预告:下一篇将讲解线与线交点的求解,欢迎继续关注!
(版权所有,转载请标明出处)
分类: ArcGIS Server, Silverlight
绿色通道: 好文要顶 关注我 收藏该文 与我联系
在前一篇的博文中说到了线与面交点的求解,其中使用的方法是利用GeometryService的Simplify+Intersect服务,通过获得线与面的交点,间接的通过交线的端点得到线与面的交点。过程如下所示:
然而在上文中我们提到过一句,Geometry中的Intersect无法得到线与线的交点,尽管当我们使用Intersect求线与线相交时,可以返回相交的结果,但是无法获取交点,因为Intersect求线与线交点返回的是一个Extent为null的线要素,我们无法将其用点在地图上表达出来。
那么这里我们该怎么做呢?
这里我们用到了Geometry的另一个服务:TrimExtend(修剪扩展)
具体示例可参考:
关于TrimExtend功能的效果图:
我们发现,使用TrimExtend之后网格水平线向右方向进行了衍生,并与线要素相交了,而位于河流下方的网格则被剪切掉了。
关于裁剪和延伸的方向(比如上图是向右延伸,还可以设置向左延伸,或者双向延伸等),可以通过CurveExtension属性来设置,关于CurveExtension的属性说明参看官方文档:
需要说明的是,TrimExtend返回的结果是线要素,而返回线要素的一端就是线要素与线要素的交点(仅当线要素相交时,从上图我们会发现没有相交的要素也被返回了,因此这里还需要进一步的筛选,具体将在后面说明)
通过以上分析,我们知道只要获取返回线要素的端点就可以得到相交的结果。
下面是具体步骤:
1.构建网格(这里也可以使用某一图层的线要素,本文采用自己构建网格,求网格与线要素相交的节点)
关键代码:
a.根据两个点构造一条直线
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); }
AddLineGraphic为自定义方法,作用是将线要素添加到图层中,示例代码如下:
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); }
这里用户可以自定义网格的步长,根据绘制的矩形或多边形范围构建网格。
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="">
效果示意图:
下面就通过TrimExtend服务来实现获取交点的功能
2.调用TrimExtend功能
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); }
接下来就是对结果进行处理,上面我们说过,所有交线的一个端点即为直线的交点。下图是返回直线的结果(红色线段即为结果),这里需要注意的是
使用TrimExtend功能时,CurveExtension属性设置为NoExtendAtFrom,这样网格就会从起点向右寻找格线的交点,没有交点则向右延长,直到相交。
(1)交线的长度一定小于初始时绘制的网格的范围,即
水平方向的交线长度<|Xmax-Xmin|, Xmax,Xmin为网格X轴的最大与最小值。
垂直方向的交线长度<|Ymax-Ymin|, Ymax,Ymin为网格Y轴的最大与最小值。
(2)针对于水平和垂直网格,交点的坐标始终是最小值(当是斜线的时候,可通过获取交线的斜率,然后再求得交点)
知道了这两点,我们实现起来就I叫容易了。下面是获取结果的代码:
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); } } } }
其中CreateMapPoint方法表示通过X,Y坐标构造一个点,AddPointGraphic表示将点添加到地图中显示。
//由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); }
这样我们就求得了线与线的交点。
示例图:
a.构造的网格线
b.交点
总结:
本文使用TrimExtend来求解交点,过程是:获取TrimExtend功能返回的线段,筛选出交线,然后获得交线的端点,即为交点。本文讲解了网格是水平和垂直时相交的情况,当线段是不规则的情况下,可以通过交线的斜率来确定交点。
下一篇,将讲解构造斜线网格来求交点,欢迎继续关注。
(版权所有,转载请标明出处)
先来看看最终的效果图吧!
对于岛屿情况:
相信很多人都做过关于河流的分析,而其中多少会涉及到河流的网格划分。二这也一直是一个难点,尤其是在Web端(本文只针对Silverlight)。而就目前查阅的资料来看,关于这方面的资料少之又少。大部分的网格划分要不就是理论,要不就是基于桌面的,而桌面的网格划分要比Web容易的多,此外,还有一种情况就是先在ArcMap中划分好,在Web端查询一定区域的网格划分点。
Web端的河流网格划分有以下几个难点:
(1).如何对河流进行网格划分得到网格点(网格划分点落在河流之内)
(2).得到的网格点如何与Web地图结合,建立地理关系
(3).针对不同的河流复杂程度(例如:河流弯曲程度不一,包含岛屿等),网格划分方法同样适用
解决了以上3个问题,那么河流网格划分问题就基本可以解决了。
下面我们来看看如何解决上面的3个问题。
目前在多数的网格划分采用的是贴体网格划分,通过求解微分方程,将不规则的河流转化到X-Y坐标系下,从而便于求解,这种方式得到的网格点很好,然而繁琐,特别是在在Web端实现更加困难,同时也无法直接与地图结合。此外偏微分方程的求解还需要一定的数学功底,因此并不适合我们普通的开发人员。
在这里我们采取添加控制点——绘制多边形——等分多边形——求交线——等分交线的方式来得到最后的网格。请看下面的示意图:
a.添加控制点,得到多边形
我们绘制了一个沿着河流的多边形,接下来,我们按照设置的等分数,等分多边形,如下图所示:
这里实现的原理的,得到河流两边的控制点,根据各边的控制点,得到各边的总长度,然后根据总长度按照设置的等分数,等分两边的多边形。接着连接两边的等分点。接下来我们就需要求解这些等分线与河流的交线。如下图所示:
这样得到与河流的交线之后,我们再对每一段的交线进行等距划分,然后得到等分点,最后如下图所示:
这样我们便得到了最终的网格点。
以上过程不需要借助任何第三方控件,只需要使用ArcGIS API for Silverlight,结合GeometryService即可完成,代码量不多,实现起来也不难。
在此提供这么一种方法大家可以自己试一试。具体代码在后续的工作中将给出。
总结:
本文方法方法的优点:
1.适用于弯曲程度不一的河流,包括河流包含岛屿等情况
2.可以按照指定的点的个数,疏密情况进行网格划分。
3.可以很好的结合Web地图
该方法的缺点:
1.目前还无法在河流交叉地方进行网格划分。
2.网格线不能保证与河流的中心线垂直。
3.多边形控制点直接影响着网格的质量。
在前一篇的博文中说到了线与面交点的求解,其中使用的方法是利用GeometryService的Simplify+Intersect服务,通过获得线与面的交点,间接的通过交线的端点得到线与面的交点。过程如下所示:
然而在上文中我们提到过一句,Geometry中的Intersect无法得到线与线的交点,尽管当我们使用Intersect求线与线相交时,可以返回相交的结果,但是无法获取交点,因为Intersect求线与线交点返回的是一个Extent为null的线要素,我们无法将其用点在地图上表达出来。
那么这里我们该怎么做呢?
这里我们用到了Geometry的另一个服务:TrimExtend(修剪扩展)
具体示例可参考:
关于TrimExtend功能的效果图:
我们发现,使用TrimExtend之后网格水平线向右方向进行了衍生,并与线要素相交了,而位于河流下方的网格则被剪切掉了。
关于裁剪和延伸的方向(比如上图是向右延伸,还可以设置向左延伸,或者双向延伸等),可以通过CurveExtension属性来设置,关于CurveExtension的属性说明参看官方文档:
需要说明的是,TrimExtend返回的结果是线要素,而返回线要素的一端就是线要素与线要素的交点(仅当线要素相交时,从上图我们会发现没有相交的要素也被返回了,因此这里还需要进一步的筛选,具体将在后面说明)
通过以上分析,我们知道只要获取返回线要素的端点就可以得到相交的结果。
下面是具体步骤:
1.构建网格(这里也可以使用某一图层的线要素,本文采用自己构建网格,求网格与线要素相交的节点)
关键代码:
a.根据两个点构造一条直线
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); }
AddLineGraphic为自定义方法,作用是将线要素添加到图层中,示例代码如下:
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); }
这里用户可以自定义网格的步长,根据绘制的矩形或多边形范围构建网格。
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="">
效果示意图:
下面就通过TrimExtend服务来实现获取交点的功能
2.调用TrimExtend功能
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); }
接下来就是对结果进行处理,上面我们说过,所有交线的一个端点即为直线的交点。下图是返回直线的结果(红色线段即为结果),这里需要注意的是
使用TrimExtend功能时,CurveExtension属性设置为NoExtendAtFrom,这样网格就会从起点向右寻找格线的交点,没有交点则向右延长,直到相交。
(1)交线的长度一定小于初始时绘制的网格的范围,即
水平方向的交线长度<|Xmax-Xmin|, Xmax,Xmin为网格X轴的最大与最小值。
垂直方向的交线长度<|Ymax-Ymin|, Ymax,Ymin为网格Y轴的最大与最小值。
(2)针对于水平和垂直网格,交点的坐标始终是最小值(当是斜线的时候,可通过获取交线的斜率,然后再求得交点)
知道了这两点,我们实现起来就I叫容易了。下面是获取结果的代码:
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); } } } }
其中CreateMapPoint方法表示通过X,Y坐标构造一个点,AddPointGraphic表示将点添加到地图中显示。
//由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); }
这样我们就求得了线与线的交点。
示例图:
a.构造的网格线
b.交点
总结:
本文使用TrimExtend来求解交点,过程是:获取TrimExtend功能返回的线段,筛选出交线,然后获得交线的端点,即为交点。本文讲解了网格是水平和垂直时相交的情况,当线段是不规则的情况下,可以通过交线的斜率来确定交点。
专业从事基于C#,WinForm ,WPF,Silverlight,WCF以及MS Sql Server 2000/2005/2008/2012 Oracle 9i/10g/11g数据库系统的ERP,CRM,企业进销存等各种数据库管理系统开发。Asp.net,Asp.net mvc,Webservice,WCF, Webapi等服务程序开发。
基于Oracle MySQL MSSql postgresql各种数据库的管理系统数据同步服务。以及基于MapXtreme, Arcgis Engine ,以及基于Arcgis for silverlight/Javascript的WebGIS等相关的GIS系统二次开发。基于Windows 10 Mobile的移动端开发方案。针对各种系统的二次开发维护,并提供相关开发的技术性支持,如程序BUG解决,应用系统架构,技术难题攻克等相关技术服务。
联系方式: QQ :80163278(devgis) 邮箱:devgis@qq.com