工作中,有時候需要圖斑按照線性要素的走向排序,比如按照河流、道路中心線排序。本篇講述如何基於AddIN技術,實現圖斑根據線要素的走向進行排序。
核心接口:ICurve.QueryPointAndDistance Method
說明:extension
1.extension參數:esriSegmentExtension枚舉類型,用於指定曲線段延伸的方式,主要有esriNoExtension(曲線段不做延伸)、esriExtendTangents等。延伸方式。
2. inPoint參數:指輸入點對象,IPoint類型,即要求該點到曲線的最短距離;
3. asRatio參數:bool類型,指定從改方法得到的distanceAlongCurve參數(見後)的值是以佔曲線總長度的比例的方式輸出還是以絕對長度值輸出;
4. outPoint參數:輸出點對象, IPoint類型,即所找到的曲線上到輸入點距離最小的點,使用前只需先實例化;
5. distanceAlongCurve參數:double類型,指曲線的FromPoint(起始點)到輸出點(outPoint)的曲線長度,asRatio參數將影響改值的輸出方式(比例還是絕對長度值);
6. distanceFromCurve參數:double類型,指輸入點到曲線的最短距離;
7.bRightSide參數:bool類型,指輸入點是否在曲線的右方。
上述解釋參考 https://blog.csdn.net/Prince999999/article/details/81316418
因此,可以使用上述接口,查詢每個圖斑點距離線要素起點的距離,然後按照該要素至線要素起點距離進行排序即可。
界面設計如下:
插件根據待排序圖層要素的對應字段與 線性圖層要素的對應字段進行分別排序,並可以根據分類字段進行歸一處理。
界面代碼:
<Window x:Class="WaterAssisterToolbar.SortByLine.SortByLineFrm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="線性排序設置"
Width="300" Height="300" MaxHeight="300" MaxWidth="300">
<Grid>
<GroupBox Header="待排序圖層" HorizontalAlignment="Left" Margin="10,5,0,0" VerticalAlignment="Top" Height="114" Width="274">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="17*"/>
<ColumnDefinition Width="113*"/>
</Grid.ColumnDefinitions>
<Label Content="圖層" HorizontalAlignment="Left" Margin="27,10,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.12,0.414" Grid.ColumnSpan="2"/>
<ComboBox Name="cmbToBeSortedLayer" HorizontalAlignment="Left" Margin="33.6,11,0,0" VerticalAlignment="Top" Width="183" Grid.Column="1" SelectionChanged="cmbToBeSortedLayer_SelectionChanged"/>
<Label Content="對應字段" HorizontalAlignment="Left" Margin="10,36,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.12,0.414" Grid.ColumnSpan="2"/>
<ComboBox Name="cmbTBRelField" HorizontalAlignment="Left" Margin="33.6,37,0,0" VerticalAlignment="Top" Width="183" Grid.Column="1"/>
<Label Content="分類字段" HorizontalAlignment="Left" Margin="10,63,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.12,0.414" Grid.ColumnSpan="2"/>
<ComboBox x:Name="cmbTBClsField" HorizontalAlignment="Left" Margin="33.6,64,0,0" VerticalAlignment="Top" Width="183" Grid.Column="1"/>
</Grid>
</GroupBox>
<GroupBox Header="線性圖層參數" HorizontalAlignment="Left" Margin="10,124,0,0" VerticalAlignment="Top" Height="82" Width="274">
<Grid>
<Label Content="圖層" HorizontalAlignment="Left" Margin="25,8,0,0" VerticalAlignment="Top" RenderTransformOrigin="1.12,0.539"/>
<ComboBox Name="cmbLineLyr" HorizontalAlignment="Left" Margin="64,10,0,0" VerticalAlignment="Top" Width="187" SelectionChanged="cmbLineLyr_SelectionChanged"/>
<Label Content="對應字段" HorizontalAlignment="Left" Margin="6,34,0,0" VerticalAlignment="Top" RenderTransformOrigin="1.12,0.539"/>
<ComboBox Name="cmbLLRelField" HorizontalAlignment="Left" Margin="64,36,0,0" VerticalAlignment="Top" Width="187"/>
</Grid>
</GroupBox>
<Button Content="確定" Name="btnOk" HorizontalAlignment="Left" Margin="48,227,0,0" VerticalAlignment="Top" Width="75" Height="28" Click="btnOk_Click"/>
<Button Content="取消" Name="btnCancel" HorizontalAlignment="Left" Margin="173,227,0,0" VerticalAlignment="Top" Width="75" Height="28" Click="btnCancel_Click"/>
</Grid>
</Window>
界面端代碼:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
namespace WaterAssisterToolbar.SortByLine
{
/// <summary>
/// SortByLineFrm.xaml 的交互邏輯
/// </summary>
public partial class SortByLineFrm : Window
{
private IFeatureLayer pTBSFtLyr = null;
/// <summary>
/// 待排序圖層
/// </summary>
public IFeatureLayer PTBSFtLyr
{
get { return pTBSFtLyr; }
set { pTBSFtLyr = value; }
}
private string tbRelFiedlName;
/// <summary>
/// 關聯字段
/// </summary>
public string TbRelFiedlName
{
get { return tbRelFiedlName; }
set { tbRelFiedlName = value; }
}
private string tbClsFieldNam;
/// <summary>
/// 分類字段
/// </summary>
public string TbClsFieldNam
{
get { return tbClsFieldNam; }
set { tbClsFieldNam = value; }
}
private IFeatureLayer pLineFtLyr;
/// <summary>
/// 排序線圖層
/// </summary>
public IFeatureLayer PLineFtLyr
{
get { return pLineFtLyr; }
set { pLineFtLyr = value; }
}
private string pLineRelFieldName;
/// <summary>
/// 線圖層關聯字段
/// </summary>
public string PLineRelFieldName
{
get { return pLineRelFieldName; }
set { pLineRelFieldName = value; }
}
private IMap pMap = null;
public SortByLineFrm()
{
InitializeComponent();
pMap = ArcMap.Document.FocusMap;
GISCommonHelper.CartoLyrHelper.setFeatureLyrCombox(ref cmbToBeSortedLayer, pMap, esriGeometryType.esriGeometryPolygon);
GISCommonHelper.CartoLyrHelper.setFeatureLyrCombox(ref cmbLineLyr, pMap, esriGeometryType.esriGeometryPolyline);
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
if (pLineFtLyr == null || pTBSFtLyr == null)
{
MessageBox.Show("請設置圖層");
return;
}
if (cmbTBRelField.SelectedIndex == -1)
{
MessageBox.Show("請設置關聯字段");
return;
}
else
{
tbRelFiedlName = cmbTBRelField.SelectedValue.ToString();
}
if (cmbTBClsField.SelectedIndex == -1)
{
MessageBox.Show("請設置分類字段");
return;
}
else
{
tbClsFieldNam = cmbTBClsField.SelectedValue.ToString();
}
if (cmbLLRelField.SelectedIndex == -1)
{
MessageBox.Show("請設置線圖層關聯字段");
return;
}
else
{
pLineRelFieldName = cmbLLRelField.SelectedValue.ToString();
}
this.DialogResult = true;
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
}
private void cmbToBeSortedLayer_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbToBeSortedLayer.SelectedIndex != -1)
{
pTBSFtLyr = cmbToBeSortedLayer.SelectedValue as IFeatureLayer;
GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbTBRelField, pTBSFtLyr.FeatureClass.Fields);
GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbTBClsField, pTBSFtLyr.FeatureClass.Fields, true);
}
}
private void cmbLineLyr_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbLineLyr.SelectedIndex != -1)
{
pLineFtLyr = cmbLineLyr.SelectedValue as IFeatureLayer;
GISCommonHelper.CartoFieldHelper.setFieldCombox(ref cmbLLRelField, pLineFtLyr.FeatureClass.Fields);
}
}
}
}
邏輯代碼:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using System.Windows.Forms;
using System.Data;
namespace WaterAssisterToolbar.SortByLine
{
public class BtnSortByLine : ESRI.ArcGIS.Desktop.AddIns.Button
{
public BtnSortByLine()
{
}
protected override void OnClick()
{
try
{
SortByLineFrm sbf = new SortByLineFrm();
if (sbf.ShowDialog() == true)
{
//開始執行
if (sbf.PTBSFtLyr.FeatureClass.Fields.FindField("LocationId") == -1)
{
IClass pCLS = sbf.PTBSFtLyr.FeatureClass as IClass;
IFieldEdit pfedit = new FieldClass();
pfedit.Name_2 = "LocationId";
pfedit.Type_2 = esriFieldType.esriFieldTypeInteger;
pCLS.AddField(pfedit as IField);
}
//對所有的排序線進行遍歷,根據排序線的對應字段,檢索屬於該排序線的要素,分類統計,之後計算每個
//要素對該排序線起點的距離,最後,對距離集合進行排序
IFeatureCursor pLineCursor = sbf.PLineFtLyr.FeatureClass.Search(null, false);
IFeature pLineFeature = pLineCursor.NextFeature();
DataTable dt = new DataTable(); //距離表
dt.Columns.Add("OID", typeof(int));
dt.Columns.Add("distance", typeof(double));
while (pLineFeature != null)
{
string mulitstr;
List<string> fenleiValues = new List<string>();
ISQLSyntax psqls = (ISQLSyntax)(((IDataset)sbf.PTBSFtLyr.FeatureClass).Workspace);
mulitstr = psqls.GetSpecialCharacter(esriSQLSpecialCharacters.esriSQL_WildcardManyMatch);
if (sbf.TbClsFieldNam == "")
{
fenleiValues.Add(mulitstr);
}
else
{
IDataStatistics DS = new DataStatisticsClass();
DS.Field = sbf.TbClsFieldNam;
DS.Cursor = (ICursor)sbf.PTBSFtLyr.FeatureClass.Search(null, false);
System.Collections.IEnumerator enumerator;
enumerator = DS.UniqueValues; //得到唯一值
enumerator.Reset();
//List<string> tmpzm = new List<string>();
while (enumerator.MoveNext())
{
object fieldvalue = enumerator.Current;
fenleiValues.Add(fieldvalue.ToString());
}
}
for (int fi = 0; fi < fenleiValues.Count; fi++)
{
//處理每一種分類情況下,分類檢索每一類要素,排序
IQueryFilter pqf = new QueryFilterClass();
if (fenleiValues[fi] == mulitstr)
{
pqf.WhereClause = "1=1";
}
else
{
//某條線下的某一類
pqf.WhereClause = string.Format("{0} = \'{1}\' and {2}=\'{3}\'",
sbf.TbClsFieldNam, fenleiValues[fi], sbf.TbRelFiedlName, pLineFeature.get_Value(pLineFeature.Fields.FindField(sbf.PLineRelFieldName)));
}
IFeatureCursor pSortFeatureCursor = sbf.PTBSFtLyr.FeatureClass.Search(pqf, true);
IFeature pSortFeature = pSortFeatureCursor.NextFeature();
dt.Rows.Clear();
while (pSortFeature != null)
{
DataRow dr = dt.NewRow();
dr[0] = pSortFeature.OID;
IEnvelope pEnv = pSortFeature.Shape.Envelope;
IPoint pnt = new PointClass();
pnt.PutCoords((pEnv.XMax+pEnv.XMin)/2,(pEnv.YMax+pEnv.YMin)/2);
dr[1] = GISCommonHelper.GeometryHelper.getDisAloneLine2FromPoint(pnt, (IPolyline)pLineFeature.Shape);
//; getDis2FromPointOfLine(pSortFeature.Shape, (IPolyline)pLineFeature.Shape);
dt.Rows.Add(dr);
pSortFeature = pSortFeatureCursor.NextFeature();
}
DataView dv = dt.DefaultView;
dv.Sort = "distance asc"; //按照距離升序排序
DataTable sorttable = dv.ToTable();
//將排序結果寫入shp
for (int i = 0; i < sorttable.Rows.Count; i++)
{
IFeature ptmpFeature = sbf.PTBSFtLyr.FeatureClass.GetFeature((int)sorttable.Rows[i][0]);
ptmpFeature.set_Value(ptmpFeature.Fields.FindField("LocationId"), i + 1);
ptmpFeature.Store();
}
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
}
pLineFeature = pLineCursor.NextFeature();
}
}
}
catch (Exception ex)
{
MessageBox.Show("發生異常:"+ex.Message);
}
}
protected override void OnUpdate()
{
}
}
}
GeometryHelper
#region 獲取點到線段起點的距離
/// <summary>
/// 獲取點到線段起點的距離
/// </summary>
/// <param name="point"></param>
/// <param name="polyline"></param>
public static double getDisAloneLine2FromPoint(IPoint point,IPolyline polyline)
{
return getDisAloneLine2FromPoint(point, (IPolycurve)polyline);
}
/// <summary>
/// 獲取點到線段起點的距離以及點在線的左側還是右側
/// </summary>
/// <param name="point"></param>
/// <param name="polyline"></param>
/// <param name="isRightSide"></param>
/// <returns></returns>
public static double getDisAloneLine2FromPoint(IPoint point, IPolyline polyline,ref bool isRightSide)
{
return getDisAloneLine2FromPoint(point, (IPolycurve)polyline, ref isRightSide);
}
public static double getDisAloneLine2FromPoint(IPoint point, IPolycurve pPolycurve)
{
bool bRightSide = false;
return getDisAloneLine2FromPoint(point, pPolycurve,ref bRightSide);
}
/// <summary>
/// 獲取點到線段起點的距離以及點在線的左側還是右側
/// </summary>
/// <param name="point"></param>
/// <param name="pPolycurve"></param>
/// <param name="bRightSide"></param>
/// <returns></returns>
public static double getDisAloneLine2FromPoint(IPoint point, IPolycurve pPolycurve, ref bool bRightSide)
{
double distanceAlongCurve = 0;//該點在曲線上最近的點距曲線起點的距離
double distanceFromCurve = 0;//該點到曲線的直線距離
bRightSide = false;
IPoint outPoint = new PointClass();
bool asRatio = false; //asRatio:byval方式,bool類型,表示上面兩個參數給定的長度是以絕對距離的方式給出還是以佔曲線總長度的比例的方式給出
pPolycurve.QueryPointAndDistance(esriSegmentExtension.esriNoExtension, point, asRatio, outPoint, ref distanceAlongCurve, ref distanceFromCurve, ref bRightSide);
return distanceAlongCurve;
}
#endregion