动态分段 |
应用场景一:
现有某城市道路的数据,根据某一时刻的某些道路路段出现拥堵和车辆较多现象,需要在道路分布图上显示出道路的路况,以提示驾驶人员避免进入拥堵路段,选择合适的行驶路线。
应用场景二:
现有一段时期内某条道路上发生的交通事故的数据,记录了事故的发生地、发生时的天气、车速、受伤和死亡人数等。现需要将这些事故发生时的天气显示在地图上,用以研究交通事故的发生与当时的天气状况是否有一定的关系。
本实例使用的是已经制作完成的SampleData数据,位于SuperMap组件产品安装目录下的SampleData中LinearReferencing文件夹中的LinearReferencing.udb来实现上一节中提到的动态分段的应用。
本实例主要使用到LinearReferencing.udb中的数据集有:Roads(已测得的某城市道路数据)、Roads_M(已知的与Roads对应的路由数据)、PointEventTab(发生在该城市部分道路的交通事故事件表)、LineEventTab(该城市道路的线事件表)。本示范程序将针对以上数据进行分析处理,主要包括:
1. 根据已经测得的线数据(Roads),使用线长度方式生成路由;
2. 根据事件表和生成的路由来创建对应事件的空间数据;
3. 对事件表进行融合,以去除冗余数据;
3. 生成空间数据之后,修改事件表中事件的刻度值或路由ID等信息,以观察事件表与空间数据的联动。
注意,在使用下面的实例代码时,建议对示范数据进行拷贝,对拷贝数据进行操作,以免由于应用实例更改数据导致原数据无法恢复。
本实例使用Visual C#语言来实现。首先打开Microsoft Visual Studio,然后新建一个Visual C#语言的“Windows窗体应用程序”类型的项目,并添加相应的程序集引用,包括SuperMap.Data、SuperMap.Mapping和SuperMap.Analyst.SpatialAnalyst。该项目中自动生成了一个名为“Form1”的窗体。在开始编码之前,首先添加一个ToolStrip控件和一个MapControl控件到窗体上,然后在ToolStrip上添加四个按钮。所添加的控件及相关属性的设置如表 11.1所示:
表 11.1 添加的控件及其属性设置
控件类型 |
name |
text |
Dock |
父控件 |
MapControl |
mapControl |
|
Fill |
|
ToolStrip |
toolStrip |
|
Top |
|
ToolStripButton |
buttonGenerateRoutes |
生成路由数据集 |
|
toolStrip |
ToolStripButton |
buttonGenerateLines |
生成道路事件空间数据 |
|
toolStrip |
ToolStripButton |
buttonModifyAttribute |
动态展示路况 |
|
toolStrip |
ToolStripButton |
buttonGeneratePoints |
生成事故点空间数据 |
|
toolStrip |
ToolStripButton |
buttonEventTableDissolve |
事件表融合 |
|
toolStrip |
下面开始编码实现:
1. 打开Form1.cs,在开始部分添加命名空间的引用:
using SuperMap.Data; using SuperMap.Mapping; using SuperMap.Analyst.SpatialAnalyst; |
2. 声明工作空间和数据源全局变量,并在Form1的FormLoad事件中,打开数据源并绑定工作空间和地图控件。
private Workspace m_workspace; private Datasource m_datasource;
private void Form1_Load(object sender, EventArgs e) { m_workspace = new Workspace();
//打开数据源 String filePath = @"..\..\SampleData\LinearReferencing\LinearReferencing.udb"; DatasourceConnectionInfo datasourceInfo = new DatasourceConnectionInfo(filePath, "动态分段", ""); datasourceInfo.EngineType = EngineType.UDB; m_datasource = m_workspace.Datasources.Open(datasourceInfo);
//绑定工作空间与地图控件 mapControl.Map.Workspace = m_workspace;
mapControl.Map.IsAntialias = true; } |
3. 生成路由数据。
生成路由有四种方式:线参考点刻度、线单字段、线双字段和线长度方式。本实例使用数据源“LinearReferencing.udb”中的线数据集“Roads”,通过“线长度”方式来生成路由数据集。运行本实例生成的路由数据如图 11‑1所示。
|
图 11‑1 生成路由数据集 |
实现的过程是,双击Form1窗体ToolStrip上的“生成路由数据集”按钮,在buttonGenerateRoutes的Click事件中添加如下代码:
private void buttonGenerateRoutes_Click(object sender, EventArgs e) { try { //获取用于生成路由的线数据 DatasetVector referenceLineM = m_datasource.Datasets["Roads"] as DatasetVector;
//构造一个 GenerateRoutesParameter 对象并设置相关参数 GenerateRoutesParameter parameter = new GenerateRoutesParameter(); parameter.Type = GenerateType.ByLength;//使用“线长度”方式来生成路由数据集 parameter.OutputDatasource = m_datasource; parameter.OutputDatasetName = "Routes"; if (m_datasource.Datasets.Contains("Routes")) { DatasetVector dynamicDataset = m_datasource.Datasets["Routes"] as DatasetVector; //在删除数据集前,先删除其动态分段关系 DynamicSegmentManager.RemoveDynamicSegmentInfos(dynamicDataset); m_datasource.Datasets.Delete("Routes"); }
//调用 GenerateRoutes 方法来生成路由数据集 DatasetVector resultRoutesDataset = LinearReferencing.GenerateRoutes(referenceLineM, parameter);
//加载到当前地图上 mapControl.Map.Layers.Add(resultRoutesDataset, true); mapControl.Map.Refresh(); } catch (Exception Err) { MessageBox.Show(Err.Message); } } |
4. 生成道路事件空间数据,并展示路况信息。
在生成路由数据集“Routes”之后,使用它和数据源“LinearReferencing.udb”中的事件表“LineEventTab”来生成对应的空间数据,并与路由数据集一起显示在地图上,根据空间数据中的路况信息来展示事件表所对应的时刻的道路拥堵状况。如图 11‑2所示,是对生成的线数据制作单值专题图并显示在地图上的效果,其中红色代表“拥堵”、黄色代表“缓行”、绿色代表“畅通”。
|
图 11‑2 生成道路空间数据并展示路况 |
实现的过程是,双击Form1窗体ToolStrip上的“生成道路事件空间数据”按钮,在buttonGenerateLines的Click事件中添加如下代码:
private void buttonGenerateLines_Click(object sender, EventArgs e) { try { //获取用于生成空间数据的路由数据集 DatasetVector referenceLineM = m_datasource.Datasets["Roads_M"] as DatasetVector;
//获取用于生成空间数据的事件表数据集 //避免破坏原始数据,所以复制一份数据,用于生成空间数据 if (m_datasource.Datasets.Contains("LineEventTab_1")) { DatasetVector dynamicDataset = m_datasource.Datasets["LineEventTab_1"] as DatasetVector; //在删除数据集前,先删除其动态分段关系 DynamicSegmentManager.RemoveDynamicSegmentInfos(dynamicDataset); m_datasource.Datasets.Delete("LineEventTab_1"); } DatasetVector eventTable = m_datasource.CopyDataset(m_datasource.Datasets["LineEventTab"], "LineEventTab_1", EncodeType.None) as DatasetVector;
//构造一个 GenerateSpatialDataParameter 对象并设置相关参数 GenerateSpatialDataParameter parameter = new GenerateSpatialDataParameter(); parameter.EventTable = eventTable; parameter.EventRouteIDField = "ROUTEID"; parameter.ReferenceLineM = referenceLineM; parameter.RouteIDField = "ROUTEID"; parameter.MeasureStartField = "STARTMEASURE"; parameter.MeasureEndField = "ENDMEASURE"; parameter.ErrorInfoField = "ERROR"; parameter.OutputDatasetName = "LineSpatialData"; if (m_datasource.Datasets.Contains("LineSpatialData")) { DatasetVector dynamicDataset = m_datasource.Datasets["LineSpatialData"] as DatasetVector; //在删除数据集前,先删除其动态分段关系 DynamicSegmentManager.RemoveDynamicSegmentInfos(dynamicDataset); m_datasource.Datasets.Delete("LineSpatialData"); }
//调用 GenerateSpatialData 方法来生成空间数据 DatasetVector resultSpatialDataset = LinearReferencing.GenerateSpatialData(parameter);
if (resultSpatialDataset != null) { MessageBox.Show("生成道路事件空间数据成功!"); }
//将生成的空间数据加载到当前地图上 mapControl.Map.Layers.Clear(); mapControl.Map.Layers.Add(resultSpatialDataset, true);
//对生成的空间数据制作专题图并加载到地图上 CreateTheme(resultSpatialDataset);
mapControl.Map.Refresh(); } catch (Exception Err) { MessageBox.Show(Err.Message); } }
private void CreateTheme(DatasetVector dataset) { //制作单值专题图,根据生成的空间数据的路况信息(BLOCK字段)显示LineEventTab对应时刻的路况 //实例化一个单值专题图对象 ThemeUnique theme = new ThemeUnique(); theme.UniqueExpression = "BLOCK";
//实例化三个单值专题图子项并设置其属性,分别对应三种路况:畅通、缓行和拥堵 ThemeUniqueItem itemN = new ThemeUniqueItem(); itemN.Caption = "畅通"; itemN.Unique = "N"; GeoStyle styleN = new GeoStyle(); styleN.LineSymbolID = 0; styleN.LineWidth = 0.6; styleN.LineColor = Color.Green; itemN.Style = styleN;
ThemeUniqueItem itemL = new ThemeUniqueItem(); itemL.Caption = "缓行"; itemL.Unique = "L"; itemL.IsVisible = true; GeoStyle styleL = new GeoStyle(); styleL.LineSymbolID = 0; styleL.LineWidth = 0.6; styleL.LineColor = Color.Yellow; itemL.Style = styleL;
ThemeUniqueItem itemM = new ThemeUniqueItem(); itemM.Caption = "拥堵"; itemM.Unique = "M"; itemM.IsVisible = true; GeoStyle styleM = new GeoStyle(); styleM.LineWidth = 0.6; styleM.LineColor = Color.Red; styleM.LineSymbolID = 0; itemM.Style = styleM;
theme.Add(itemN); theme.Add(itemL); theme.Add(itemM);
mapControl.Map.Layers.Add(dataset, theme, true); mapControl.Map.Refresh(); } |
5. 动态展示。
在生成空间数据之后,路由、事件表和空间数据三者建立了临时的联动关系(详细介绍请阅读第10.2小节)。在上一步中根据线事件表LineEventTab_1生成了对应的空间数据LineSpatialData,本实例将通过修改LineEventTab中道路路况属性来展示事件表与空间数据的联动,体现动态分段能够动态展示属性信息的特性。在实例中将事件表LineEventTab_1中“BLOCK”属性值为“M”的事件修改为“N”,可以看到对LineSpatialData制作的专题图在刷新地图后发生了变化,证明修改事件表后,其对应的空间数据也发生了变化。图 11‑3为运行结果,与图 11‑2对比,很明显LineSpatialData随着事件表的修改自动发生了变化。
|
图 11‑3 动态展示路况信息 |
实现的过程是,双击Form1窗体ToolStrip上的“动态展示路况”按钮,在buttonModifyAttribute的Click事件中添加如下代码:
private void buttonModifyAttribute_Click(object sender, EventArgs e) { try { //获取要修改的事件表,这里获取一个线事件表 DatasetVector eventTable = m_datasource.Datasets["LineEventTab_1"] as DatasetVector; //获取该事件表的记录集 Recordset recordset = eventTable.Query("BLOCK='M'", CursorType.Dynamic);
//将事件表中路况为“拥堵”的事件修改为“缓行” recordset.MoveFirst(); for (Int32 i = 0; i < recordset.RecordCount; i++) { recordset.Edit(); recordset.SetFieldValue("BLOCK", "N"); recordset.Update(); recordset.MoveNext(); } recordset.Close(); recordset.Dispose();
//刷新地图 //注意,需确保当前地图上加载了在“生成道路事件空间数据”功能中 //生成的空间数据和专题图图层,才能观察到变化 mapControl.Map.Refresh(); } catch (Exception Err) { MessageBox.Show(Err.Message); } } |
6. 生成点事件空间数据。
点事件表PointEventTab中存储了发生在部分路由上的交通事故事件。使用路由数据集Routes和PointEventTab事件表可以生成对应的事故点空间数据。将生成的空间数据与路由数据一起加载到地图上,可以展示交通事故发生的位置,实现的效果如图 11‑4所示。
|
图 11‑4 生成事故点空间数据并加载到地图上 |
要实现这一功能,需要在buttonGeneratePoints的Click事件中添加如下代码:
private void buttonGeneratePoints_Click(object sender, EventArgs e) { try { //获取用于生成空间数据的事件表数据集和路由数据集 DatasetVector eventTable = m_datasource.Datasets["PointEventTab"] as DatasetVector; DatasetVector referenceLineM = m_datasource.Datasets["Roads_M"] as DatasetVector;
//构造一个 GenerateSpatialDataParameter 对象并设置相关参数 GenerateSpatialDataParameter parameter = new GenerateSpatialDataParameter(); parameter.EventTable = eventTable; parameter.EventRouteIDField = "ROUTEID"; parameter.ReferenceLineM = referenceLineM; parameter.RouteIDField = "ROUTEID"; parameter.MeasureField = "MEASURE"; parameter.ErrorInfoField = "ERROR"; parameter.OutputDatasetName = "PointSpatialData"; if (m_datasource.Datasets.Contains("PointSpatialData")) { DatasetVector dynamicDataset = m_datasource.Datasets["PointSpatialData"] as DatasetVector; //在删除数据集前,先删除其动态分段关系 DynamicSegmentManager.RemoveDynamicSegmentInfos(dynamicDataset); m_datasource.Datasets.Delete("PointSpatialData"); }
//调用 GenerateSpatialData 方法来生成空间数据 DatasetVector resultSpatialDataset = LinearReferencing.GenerateSpatialData(parameter);
if (resultSpatialDataset != null) { MessageBox.Show("生成事故点空间数据成功!"); }
//设置图层显示风格 LayerSettingVector layerSettingVector = new LayerSettingVector(); GeoStyle style = new GeoStyle(); style.LineColor = Color.Red; style.MarkerSize = new Size2D(4, 4); style.MarkerSymbolID = 0; layerSettingVector.Style = style;
//加载路由数据和生成的点事件的空间数据到当前地图上 mapControl.Map.Layers.Clear(); mapControl.Map.Layers.Add(referenceLineM, false); mapControl.Map.Layers.Add(resultSpatialDataset, layerSettingVector, true); mapControl.Map.Refresh(); } catch (Exception Err) { MessageBox.Show(Err.Message); } } |
7. 事件表融合。
事件表融合可以去除冗余数据,也可以用于按不同属性拆分为多个事件表,详细内容请阅读第9.2节。本实例示范了如何融合事件表。实现的过程是,在按钮buttonEventTableDissove的Click事件中添加如下代码:
private void buttonEventTableDissove_Click(object sender, EventArgs e) { try { //获取要融合的事件表,这里获取一个线事件表 DatasetVector eventTable = m_datasource.Datasets["LineEventTab"] as DatasetVector;
//构造一个 RouteEventsParameter 对象并设置输入参数 RouteEventsParameter inputParameter = new RouteEventsParameter(); inputParameter.EventDataset = eventTable; inputParameter.RouteIDField = "ROUTEID"; inputParameter.MeasureStartField = "STARTMEASURE"; inputParameter.MeasureEndField = "ENDMEASURE";
//构造一个 RouteEventsParameter 对象并设置融合结果参数 RouteEventsParameter resultParameter = new RouteEventsParameter(); resultParameter.RouteIDField = "ROUTEID"; resultParameter.MeasureStartField = "STARTMEASURE"; resultParameter.MeasureEndField = "ENDMEASURE";
//指定事件表中的融合字段 String[] dissolveFields = { "RATING" }; //指定事件表中的统计字段及统计方法 String[] statisticsFields = { "ADT" }; StatisticsType[] statisticsModes = { StatisticsType.Mean };
String outputDatasetName = "DissolvedTab"; if (m_datasource.Datasets.Contains("DissolvedTab")) { DatasetVector dynamicDataset = m_datasource.Datasets["DissolvedTab"] as DatasetVector; //在删除数据集前,先删除其动态分段关系 DynamicSegmentManager.RemoveDynamicSegmentInfos(dynamicDataset); m_datasource.Datasets.Delete("DissolvedTab"); }
//调用 DissolveRouteEvents 方法实现事件表的融合,融合类型设置为”连接“ DatasetVector resultDissolveTable = LinearReferencing.DissolveRouteEvents(inputParameter, dissolveFields, EventDissolveType.EventConcatenate, m_datasource, outputDatasetName, resultParameter, statisticsFields, statisticsModes, 0.001);
if (resultDissolveTable != null) { MessageBox.Show("融合成功!"); } } catch (Exception Err) { MessageBox.Show(Err.Message); } } |
8. 最后,在窗体关闭时,释放相关资源。
private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { try { mapControl.Dispose(); m_workspace.Dispose(); } catch (Exception Err) { MessageBox.Show(Err.Message); } } |