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

ArcGIS API for Silverlight 使用GeometryService求解线与面的交点(一),第1张

概述最近在做项目的时候遇到一个问题,大致情况如下: 已知河流的面要素,需要根据用户输入的矩形以及设定的步长对河流进行网格划分,并得到网格与两边河岸的交点。 查了查资料,发现原生的ArcGIS API for Silverlight并没有提供实现该功能的借口,但是GeometryService提供了一个类似的功能:Intersect. Intersect:其相交的情况有如下三种: 这里需要注意的时最后一

最近在做项目的时候遇到一个问题,大致情况如下:

已知河流的面要素,需要根据用户输入的矩形以及设定的步长对河流进行网格划分,并得到网格与两边河岸的交点。

查了查资料,发现原生的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);                  Createline(mp1,mp2);              }              //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"))              };          }

以上的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,yMin +i * ystep);                  mp2 = CreateMapPoint(xMin + Nx * xstep,yMin + i * ystep);                  //mp2 = CreateMapPoint(xMax,mp2);              }

这里我们将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<GraphicsEventArgs>(geometryService_IntersectCompleted);              geometryService.SimplifyCompleted += new EventHandler<GraphicsEventArgs>(geometryService_SimplifyCompleted);                            geometryService.Failed += new EventHandler<TaskFailedEventArgs>(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<DrawEventArgs>(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 API for Silverlight 使用GeometryService求解线与面的交点(一)全部内容,希望文章能够帮你解决ArcGIS API for Silverlight 使用GeometryService求解线与面的交点(一)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1022881.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存