在前一篇的博文中說到了線與面交點的求解,其中使用的方法是利用GeometryService的Simplify+Intersect服務,通過獲得線與面的交點,間接的通過交線的端點得到線與面的交點。過程如下所示:

然而在上文中我們提到過一句,Geometry中的Intersect無法得到線與線的交點,盡管當我們使用Intersect求線與線相交時,可以返回相交的結果,但是無法獲取交點,因為Intersect求線與線交點返回的是一個Extent為null的線要素,我們無法將其用點在地圖上表達出來。
那么這里我們該怎么做呢?
這里我們用到了Geometry的另一個服務:TrimExtend(修剪擴展)
具體示例可參考:
http://help.arcgis.com/en/webapi/silverlight/samples/start.htm#TrimExtend
關於TrimExtend功能的效果圖:

我們發現,使用TrimExtend之后網格水平線向右方向進行了衍生,並與線要素相交了,而位於河流下方的網格則被剪切掉了。
關於裁剪和延伸的方向(比如上圖是向右延伸,還可以設置向左延伸,或者雙向延伸等),可以通過CurveExtension屬性來設置,關於CurveExtension的屬性說明參看官方文檔:
http://help.arcgis.com/en/webapi/silverlight/apiref/api_start.htm
需要說明的是,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(xMin + Nx * xstep, 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"))
};
}
效果示意圖:

下面就通過TrimExtend服務來實現獲取交點的功能
2.調用TrimExtend功能
private void intersectCenterLineButton_Click(object sender, RoutedEventArgs e)
{
//存儲線要素的集合
List<ESRI.ArcGIS.Client.Geometry.Polyline> polyLineListGrid = new List<ESRI.ArcGIS.Client.Geometry.Polyline>();
List<ESRI.ArcGIS.Client.Geometry.Polyline> polyLineListriver = new List<ESRI.ArcGIS.Client.Geometry.Polyline>();
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功能返回的線段,篩選出交線,然后獲得交線的端點,即為交點。本文講解了網格是水平和垂直時相交的情況,當線段是不規則的情況下,可以通過交線的斜率來確定交點。
下一篇,將講解構造斜線網格來求交點,歡迎繼續關注。
(版權所有,轉載請標明出處)
