工作中,有时候需要图斑按照线性要素的走向排序,比如按照河流、道路中心线排序。本篇讲述如何基于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