====== Source Code Routing Index Generator.zip ====== ====RoutingIndexGenerator.cs==== using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; using System.Xml.Linq; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.Routing; namespace RoutingIndexGenerator { public partial class RoutingIndexGenerator : Form { // If the area is more than 1,000,000 square miles, we would throw a warning as the index generating might take too much time. private double warningAreaInSquareMiles = 1000000; private ConfigModel currentConfigModel; private RoutingIndexBuilder routingIndexbuilder; private Dictionary configModelsDictionary; public RoutingIndexGenerator() { InitializeComponent(); configModelsDictionary = new Dictionary(); if (File.Exists("InitConfig.xml")) { ConfigModel model = new ConfigModel("InitConfig.xml"); string key = model.FileType.Split('|').Last(); configModelsDictionary.Add(key, model); } if (File.Exists("WkbConfig.xml")) { ConfigModel model = new ConfigModel("WkbConfig.xml"); string key = model.FileType.Split('|').Last(); configModelsDictionary.Add(key, model); } // We will always load shapefile configuration by default, even there is no configuration file. if (!configModelsDictionary.Any(i => i.Value.FileType.Contains("*.shp"))) { ConfigModel model = new ConfigModel(XDocument.Parse(Properties.Resources.DefaultConfig)); string key = model.FileType.Split('|').Last(); configModelsDictionary.Add(key, model); } } private void btnBrowseSqliteFile_Click(object sender, EventArgs e) { OpenFileDialog fileDialog = new OpenFileDialog(); fileDialog.Filter = GetSelectFileFilter(); if (fileDialog.ShowDialog() == DialogResult.OK) { string currentKey = string.Format("*.{0}", fileDialog.FileName.Split('.').Last()); currentConfigModel = configModelsDictionary[currentKey]; // Get the bounding box of the feature source. FeatureSource featureSource = null; if (currentConfigModel.FileType.EndsWith(".sqlite", StringComparison.InvariantCultureIgnoreCase)) { featureSource = new SqliteFeatureSource(string.Format("Data Source={0};Version=3;", fileDialog.FileName), currentConfigModel.TableName, currentConfigModel.FeatureIdColumn, currentConfigModel.GeometryColumnName); } if (currentConfigModel.FileType.EndsWith(".shp", StringComparison.InvariantCultureIgnoreCase)) { featureSource = new ShapeFileFeatureSource(fileDialog.FileName); ((ShapeFileFeatureSource)featureSource).RequireIndex = false; } if (currentConfigModel.FileType.EndsWith(".wkb", StringComparison.InvariantCultureIgnoreCase)) { featureSource = new WkbFileFeatureSource(fileDialog.FileName); } featureSource.Open(); WellKnownType wellKnownType = featureSource.GetFirstFeaturesWellKnownType(); if (wellKnownType != WellKnownType.Line && wellKnownType != WellKnownType.Multiline) { ShowErrorMessageBox(); return; } txtDataProviderFilePath.Text = fileDialog.FileName; txtRoutableFile.Text = Path.ChangeExtension(fileDialog.FileName, ".routable.shp"); txtIndexFilePath.Text = Path.ChangeExtension(fileDialog.FileName, ".routable.rtg"); RectangleShape boundingBox = featureSource.GetBoundingBox(); txtUpperLeftX.Text = string.Format("{0},{1}", boundingBox.UpperLeftPoint.X, boundingBox.UpperLeftPoint.Y); txtLowerRightX.Text = string.Format("{0},{1}", boundingBox.LowerRightPoint.X, boundingBox.LowerRightPoint.Y); // Get the columns of the feature source. Collection columns = featureSource.GetColumns(); featureSource.Close(); if (boundingBox.UpperLeftPoint.X < -180 || boundingBox.UpperLeftPoint.Y > 90 || boundingBox.LowerRightPoint.X > 180 || boundingBox.LowerRightPoint.Y < -90) { cmbGeographyUnit.SelectedIndex = 2; } else { cmbGeographyUnit.SelectedIndex = 0; } InitCombColumns(cmbOnewayRoadColumn, columns, currentConfigModel.IdentifierColumnName); InitCombColumns(cmbOnewayIndicatorColumn, columns, currentConfigModel.IndicatorColumnName); InitCombColumns(cmbRoadClassColumn, columns, currentConfigModel.SpeedColumnName); InitCombColumns(cmbSpeedColumn, columns, currentConfigModel.SpeedColumnName); InitCombColumns(cmbClosedRoadColumn, columns, currentConfigModel.ClosedColumnName); if (currentConfigModel.RoutingIndexType == 0) { rbtnClassSpeed.Checked = true; tbOptions.Enabled = true; } cmbRouteIndexType.SelectedIndex = currentConfigModel.RoutingIndexType; cmbSpeedUnit.SelectedIndex = currentConfigModel.SpeedUnit; txtDefaultSpeed.Text = currentConfigModel.DefaultSpeed.ToString(); txtOneWayRoadValue.Text = currentConfigModel.IdentifierColumnValue; txtFromToValue.Text = currentConfigModel.IndicatorStartEndValue; btnGenerate.Enabled = true; } } private void btnGenerate_Click(object sender, EventArgs e) { RoutingIndexBuilderParameters buildRoutingIndexParameters = GetIndexBuildingParameters(); if (buildRoutingIndexParameters == null) { return; } //create a new RoutingIndexFileBuilder. routingIndexbuilder = new RoutingIndexBuilder(buildRoutingIndexParameters); //Add an event to display refresh the ProgressBar. routingIndexbuilder.ExtractingSqliteDatabase += routingIndexbuilder_ExtractingSqliteDatabase; routingIndexbuilder.BuildingRoutableSegment += routingIndexbuilder_BuildingRoutableSegment; routingIndexbuilder.BuildingRouteRTSegment += routingIndexbuilder_BuildingRouteSegment; routingIndexbuilder.BuildingIndexFinished += routingIndexbuilder_BuildingIndexFinished; routingIndexbuilder.BuildingShapeFileIndex += routingIndexbuilder_BuildingShapeFileIndex; btnGenerate.Enabled = false; //Start building index file(.rtg). Task.Factory.StartNew(() => { routingIndexbuilder.ExtractRoadsFromSqliteToShapefileWithLowerMemory(currentConfigModel); routingIndexbuilder.StartBuildingRoutableFile(); routingIndexbuilder.StartBuildingRtgFile(); }); } private void routingIndexbuilder_BuildingShapeFileIndex(object sender, RoutingIndexBuilderEventArgs e) { if (e.ProcessedRecordCount % 100 == 0 || e.ProcessedRecordCount == e.TotalRecordCount) { this.BeginInvoke(new Action(() => { lblProcessingMessage.Text = string.Format("Building shape file index: {0}/{1}", e.ProcessedRecordCount, e.TotalRecordCount); pgbBuildProgress.Value = (int)(((double)e.ProcessedRecordCount / (double)e.TotalRecordCount) * 100); })); } } private void routingIndexbuilder_BuildingIndexFinished(object sender, EventArgs e) { this.BeginInvoke(new Action(() => { btnGenerate.Enabled = true; MessageBox.Show(this, "Generation Completed!", "Completed"); })); } private void routingIndexbuilder_ExtractingSqliteDatabase(object sender, RoutingIndexBuilderEventArgs e) { if (e.ProcessedRecordCount % 100 == 0 || e.ProcessedRecordCount == e.TotalRecordCount) { this.BeginInvoke(new Action(() => { lblProcessingMessage.Text = string.Format("Generating Index (1/3): Extracting source file: {0}/{1}", e.ProcessedRecordCount, e.TotalRecordCount); pgbBuildProgress.Value = (int)(((double)e.ProcessedRecordCount / (double)e.TotalRecordCount) * 100); ; })); } } private void routingIndexbuilder_BuildingRoutableSegment(object sender, RoutingIndexBuilderEventArgs e) { if (e.ProcessedRecordCount % 100 == 0 || e.ProcessedRecordCount == e.TotalRecordCount) { this.BeginInvoke(new Action(() => { lblProcessingMessage.Text = string.Format("Generating Index (2/3) Building Routable shape file: {0}/{1}", e.ProcessedRecordCount, e.TotalRecordCount); pgbBuildProgress.Value = (int)(((double)e.ProcessedRecordCount / (double)e.TotalRecordCount) * 100); })); } } private void routingIndexbuilder_BuildingRouteSegment(object sender, RoutingIndexBuilderEventArgs e) { if (e.ProcessedRecordCount % 100 == 0 || e.ProcessedRecordCount == e.TotalRecordCount) { this.BeginInvoke(new Action(() => { lblProcessingMessage.Text = string.Format("Generating Index (3/3) Building rtg file: {0}/{1}", e.ProcessedRecordCount, e.TotalRecordCount); pgbBuildProgress.Value = (int)(((double)e.ProcessedRecordCount / (double)e.TotalRecordCount) * 100); })); } } private void btnSaveRtgFile_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.FileName = Path.ChangeExtension(txtDataProviderFilePath.Text, ".routable.rtg"); ; saveFileDialog.Filter = "Routing Index File(*.rtg)|*.rtg"; saveFileDialog.DefaultExt = ".rtg"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { txtIndexFilePath.Text = saveFileDialog.FileName; } } private void btnSaveRoutableFile_Click(object sender, EventArgs e) { SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.FileName = Path.ChangeExtension(txtDataProviderFilePath.Text, ".Routable.shp"); saveFileDialog.Filter = "Shape File(*.shp)|*.shp"; saveFileDialog.DefaultExt = ".shp"; if (saveFileDialog.ShowDialog() == DialogResult.OK) { this.txtRoutableFile.Text = saveFileDialog.FileName; } } private void SpeedTypeRadioButton_CheckedChanged(object sender, EventArgs e) { lblRoadClassColumn.Enabled = rbtnClassSpeed.Checked; cmbRoadClassColumn.Enabled = rbtnClassSpeed.Checked; dgvRoadClassSpeed.Enabled = rbtnClassSpeed.Checked; cmbSpeedColumn.Enabled = rbtnRoadSpeed.Checked; } private void cmbRouteIndexType_SelectedIndexChanged(object sender, EventArgs e) { bool considerringSpeedLimit = cmbRouteIndexType.SelectedItem.ToString() == "Fastest"; panelRouteSpeedOptions.Enabled = considerringSpeedLimit; } private void cmbSpeedUnit_SelectedIndexChanged(object sender, EventArgs e) { if (cmbSpeedUnit.SelectedIndex == 0) { txtDefaultSpeed.Text = "50"; } else { txtDefaultSpeed.Text = "30"; } BindRoadClassSpeed(); } private void cmbOnewayRoadColumn_SelectedIndexChanged(object sender, EventArgs e) { bool oneWayIdentifierIsNotNull = (cmbOnewayRoadColumn.SelectedItem.ToString() != string.Empty); label8.Enabled = oneWayIdentifierIsNotNull; txtOneWayRoadValue.Enabled = oneWayIdentifierIsNotNull; panelOneWayIndicator.Enabled = oneWayIdentifierIsNotNull; } private void cmbOnewayIndicatorColumn_SelectedIndexChanged(object sender, EventArgs e) { bool oneWayIndicatorColumnNotNull = (cmbOnewayIndicatorColumn.SelectedItem.ToString() != string.Empty); panelOneWayIndicatorDetail.Enabled = oneWayIndicatorColumnNotNull; } private void cmbClosedRoadColumn_SelectedIndexChanged(object sender, EventArgs e) { bool closedRoadColumnNotNull = (cmbClosedRoadColumn.SelectedItem.ToString() != string.Empty); panelClosedRoad.Enabled = closedRoadColumnNotNull; } private void cmbRoadClassColumn_SelectedIndexChanged(object sender, EventArgs e) { BindRoadClassSpeed(); } private void btnClose_Click(object sender, EventArgs e) { if (routingIndexbuilder != null) { routingIndexbuilder.CancelBuildingRtgFile(); } this.Close(); } private RoutingIndexBuilderParameters GetIndexBuildingParameters() { RoutingIndexBuilderParameters buildRoutingIndexParameters = new RoutingIndexBuilderParameters(); buildRoutingIndexParameters.SourceSqlitePathName = txtDataProviderFilePath.Text; buildRoutingIndexParameters.GeographyUnit = (GeographyUnit)Enum.Parse(typeof(GeographyUnit), cmbGeographyUnit.SelectedItem.ToString()); buildRoutingIndexParameters.RouteIndexType = (RoutingIndexType)Enum.Parse(typeof(RoutingIndexType), cmbRouteIndexType.SelectedItem.ToString()); buildRoutingIndexParameters.RtgFilePathName = txtIndexFilePath.Text; buildRoutingIndexParameters.RoutableShapeFilePathName = txtRoutableFile.Text; buildRoutingIndexParameters.BuildRoutingDataMode = chkRebuildRtgFile.Checked ? BuildRoutingDataMode.Rebuild : BuildRoutingDataMode.DoNotRebuild; buildRoutingIndexParameters.OverrideRoutableFile = chkRebuildRoutableFile.Checked ? true : false; //Get restrict extent if specify. if (this.txtUpperLeftX.Text.Split(',').Length == 2 && this.txtUpperLeftX.Text.Split(',').Length == 2) { buildRoutingIndexParameters.RestrictExtent = new RectangleShape(double.Parse(this.txtUpperLeftX.Text.Split(',')[0]), double.Parse(this.txtUpperLeftX.Text.Split(',')[1]), double.Parse(this.txtLowerRightX.Text.Split(',')[0]), double.Parse(this.txtLowerRightX.Text.Split(',')[1])); // Check if restrict extent is too large and give it a warning if does. if (buildRoutingIndexParameters.RestrictExtent.GetArea(buildRoutingIndexParameters.GeographyUnit, AreaUnit.SquareMiles) > warningAreaInSquareMiles) { string routingAreaTooLargeWarning = "The selected area is too big and may take a long time, we suggest making the selected area smaller, do you want to continue anyway?"; if (DialogResult.No == MessageBox.Show(routingAreaTooLargeWarning, "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)) { return null; } } } else { MessageBox.Show("Either Upper Left Point or Lower Right Point is not correctly"); return null; } // Get the Speed Options. if (cmbRouteIndexType.SelectedItem.ToString() == "Fastest") { buildRoutingIndexParameters.SpeedOption.SpeedUnit = (SpeedUnit)Enum.Parse(typeof(SpeedUnit), cmbSpeedUnit.SelectedIndex.ToString()); buildRoutingIndexParameters.SpeedOption.DefaultSpeed = Convert.ToSingle(txtDefaultSpeed.Text); buildRoutingIndexParameters.SpeedOption.SpeedSourceType = rbtnClassSpeed.Checked ? RoadSpeedSourceType.BasedOnRoadType : RoadSpeedSourceType.DefinedInAttribution; buildRoutingIndexParameters.SpeedOption.SpeedColumnName = cmbSpeedColumn.Text; buildRoutingIndexParameters.SpeedOption.RoadTypeColumnName = cmbRoadClassColumn.Text; DataTable dataTable = (DataTable)dgvRoadClassSpeed.DataSource; buildRoutingIndexParameters.SpeedOption.RoadSpeeds.Clear(); foreach (DataRow dataRow in dataTable.Rows) { buildRoutingIndexParameters.SpeedOption.RoadSpeeds.Add(dataRow[0].ToString(), Convert.ToDouble(dataRow[1])); } } // Get the One way Road Options. buildRoutingIndexParameters.OneWayRoadOption.OneWayColumnName = cmbOnewayRoadColumn.Text; if (buildRoutingIndexParameters.OneWayRoadOption.OneWayColumnName != string.Empty) { buildRoutingIndexParameters.IncludeOnewayOptions = true; buildRoutingIndexParameters.OneWayRoadOption.IndicatorColumnName = cmbOnewayIndicatorColumn.Text; buildRoutingIndexParameters.OneWayRoadOption.OneWayIdentifier = txtOneWayRoadValue.Text; buildRoutingIndexParameters.OneWayRoadOption.StartToEnd = txtFromToValue.Text; buildRoutingIndexParameters.OneWayRoadOption.EndToStart = txtToFromValue.Text; } // Get the closed Road Options. buildRoutingIndexParameters.ClosedRoadOption.ClosedColumnName = cmbClosedRoadColumn.Text; buildRoutingIndexParameters.ClosedRoadOption.ClosedRoadValue = txtColsedRoad.Text; return buildRoutingIndexParameters; } private void ShowErrorMessageBox() { MessageBox.Show(this, "The chosen data doesn't include line shapes. Please choose another one.", "Invalid data", MessageBoxButtons.OK, MessageBoxIcon.Error); } private string GetSelectFileFilter() { string filter = string.Empty; string allExName = string.Empty; List fileExNames = new List(); foreach (var item in configModelsDictionary) { if (string.IsNullOrEmpty(filter)) filter += item.Value.FileType; else { filter += "|" + item.Value.FileType; } if (string.IsNullOrEmpty(allExName)) allExName += item.Key; else { allExName += ";" + item.Key; } fileExNames.Add(item.Value.FileType.Split('|').Last()); } filter += string.Format("|All Surpported Files({0})|{0}", allExName); return filter; } private void BindRoadClassSpeed() { DataTable dataTable = new DataTable(); dataTable.Columns.Add("RoadClass"); dataTable.Columns.Add("Speed"); Dictionary roadSpeed = currentConfigModel.RoadSpeedForKPH; if (cmbSpeedUnit.SelectedIndex == 1) { roadSpeed = currentConfigModel.RoadSpeedForMPH; } foreach (var item in roadSpeed) { DataRow dr = dataTable.NewRow(); dr[0] = item.Key; dr[1] = item.Value; dataTable.Rows.Add(dr); } dgvRoadClassSpeed.DataSource = dataTable; } private void InitCombColumns(ComboBox comboBox, Collection columns, string defaultValue) { comboBox.Items.Clear(); comboBox.Items.Add(""); foreach (FeatureSourceColumn item in columns) { comboBox.Items.Add(item.ColumnName); if (item.ColumnName.Equals(defaultValue, StringComparison.InvariantCultureIgnoreCase)) { comboBox.SelectedIndex = comboBox.Items.Count - 1; } } } } } ====RoutingIndexBuilder.cs==== using System; using System.Collections.ObjectModel; using System.IO; using System.Text; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.Routing; namespace RoutingIndexGenerator { public class RoutingIndexBuilder { public event EventHandler BuildingShapeFileIndex; public event EventHandler ExtractingSqliteDatabase; public event EventHandler BuildingRoutableSegment; public event EventHandler BuildingRouteRTSegment; public event EventHandler BuildingIndexFinished; private bool cancelBuilding; private int totalRecordCount; private int processedRecordCount; private Feature previousFeature; private Collection requiredColumns; private ShapeFileFeatureSource routableFeatureSource; private RoutingIndexBuilderParameters buildRtgParameter; public RoutingIndexBuilder() : this(new RoutingIndexBuilderParameters()) { } public RoutingIndexBuilder(RoutingIndexBuilderParameters buildRtgParameter) { this.buildRtgParameter = buildRtgParameter; this.buildRtgParameter.ClosedRoadOption.InitRegexes(); this.buildRtgParameter.OneWayRoadOption.InitRegexes(); RtgRoutingSource.GeneratingRoutableShapeFile += RtgRoutingSource_GeneratingRoutableShapeFile; RtgRoutingSource.BuildingRoutingData += RtgRoutingSource_BuildingRoutingData; } //This method uses lower memory than the other method, but only works with .sqlite files public void ExtractRoadsFromSqliteToShapefileWithLowerMemory(ConfigModel config) { // If don't override routable file, let's skip this step. if (buildRtgParameter.OverrideRoutableFile || !File.Exists(buildRtgParameter.RoutableShapeFilePathName)) { SqliteFeatureSource source = new SqliteFeatureSource(string.Format("Data Source={0};Version=3;", buildRtgParameter.SourceSqlitePathName), config.TableName, config.FeatureIdColumn, config.GeometryColumnName); source.Open(); RectangleShape restrictExtent = buildRtgParameter.RestrictExtent == null ? source.GetBoundingBox() : buildRtgParameter.RestrictExtent; Collection ids = new Collection(); ids = source.GetFeatureIdsInsideBoundingBox(restrictExtent); Collection dbfColumns = new Collection(); foreach (string columnName in config.ExtractRequiredColumns) { dbfColumns.Add(new DbfColumn(columnName, DbfColumnType.Character, 255, 0)); } ShapeFileFeatureSource.CreateShapeFile(ShapeFileType.Polyline, buildRtgParameter.SourceShapefilePathName, dbfColumns, Encoding.UTF8, OverwriteMode.Overwrite); ShapeFileFeatureSource target = new ShapeFileFeatureSource(buildRtgParameter.SourceShapefilePathName, ShapeFileReadWriteMode.ReadWrite); target.Open(); int processedRecordCount = 0; int totalRecordCount = ids.Count; target.BeginTransaction(); //load 10,000 features into memory at a time from the sqlite database int loadingFeatureCount = 10000; int startIndex = 0; int length = 0; string[] totalIds = new string[ids.Count]; ids.CopyTo(totalIds, 0); while (startIndex + length < ids.Count) { startIndex = startIndex + length; length = Math.Min(ids.Count - startIndex, loadingFeatureCount); string[] fetchIds = new string[length]; Array.Copy(totalIds, startIndex, fetchIds, 0, length); Collection features = source.GetFeaturesByIds(fetchIds, config.ExtractRequiredColumns); foreach (Feature feature in features) { target.AddFeature(feature); processedRecordCount++; //write 100,000 features to shapefile at a time if (processedRecordCount % 100000 == 0) { target.CommitTransaction(); target.BeginTransaction(); } OnExtractingSqliteDatabase(new RoutingIndexBuilderEventArgs(totalRecordCount, processedRecordCount)); } } target.CommitTransaction(); target.Close(); source.Close(); } } public void ExtractRoadsFromSqliteToShapefile(ConfigModel config) { // If don't override routable file, let's skip this step. if (buildRtgParameter.OverrideRoutableFile || !File.Exists(buildRtgParameter.RoutableShapeFilePathName)) { FeatureSource source = null; if (config.FileType.EndsWith(".sqlite", StringComparison.InvariantCultureIgnoreCase)) { source = new SqliteFeatureSource(string.Format("Data Source={0};Version=3;", buildRtgParameter.SourceSqlitePathName), config.TableName, config.FeatureIdColumn, config.GeometryColumnName); } else if (config.FileType.EndsWith(".shp", StringComparison.InvariantCultureIgnoreCase)) { ShapeFileFeatureSource.BuildingIndex += ShapeFileFeatureSource_BuildingIndex; ShapeFileFeatureSource.BuildIndexFile(buildRtgParameter.SourceSqlitePathName, BuildIndexMode.DoNotRebuild); source = new ShapeFileFeatureSource(buildRtgParameter.SourceSqlitePathName); } else if (config.FileType.EndsWith(".wkb", StringComparison.InvariantCultureIgnoreCase)) { source = new WkbFileFeatureSource(buildRtgParameter.SourceSqlitePathName); } source.Open(); RectangleShape restrictExtent = buildRtgParameter.RestrictExtent == null ? source.GetBoundingBox() : buildRtgParameter.RestrictExtent; Collection features = new Collection(); Collection dbfColumns = new Collection(); if (config.ExtractRequiredColumns.Count == 0) { features = source.GetFeaturesInsideBoundingBox(restrictExtent, ReturningColumnsType.AllColumns); foreach (var column in source.GetColumns()) { dbfColumns.Add(new DbfColumn(column.ColumnName, DbfColumnType.Character, column.MaxLength, 0)); } } else { features = source.GetFeaturesInsideBoundingBox(restrictExtent, config.ExtractRequiredColumns); foreach (string columnName in config.ExtractRequiredColumns) { dbfColumns.Add(new DbfColumn(columnName, DbfColumnType.Character, 255, 0)); } } ShapeFileFeatureSource.CreateShapeFile(ShapeFileType.Polyline, buildRtgParameter.SourceShapefilePathName, dbfColumns, Encoding.UTF8, OverwriteMode.Overwrite); ShapeFileFeatureSource target = new ShapeFileFeatureSource(buildRtgParameter.SourceShapefilePathName, ShapeFileReadWriteMode.ReadWrite); target.Open(); int processedRecordCount = 0; int totalRecordCount = features.Count; target.BeginTransaction(); foreach (Feature feature in features) { target.AddFeature(feature); processedRecordCount++; if (processedRecordCount % 100000 == 0) { target.CommitTransaction(); target.BeginTransaction(); } OnExtractingSqliteDatabase(new RoutingIndexBuilderEventArgs(totalRecordCount, processedRecordCount)); } target.CommitTransaction(); target.Close(); source.Close(); } } public void StartBuildingRoutableFile() { if (buildRtgParameter.OverrideRoutableFile) { ShapeFileFeatureSource sourceFeatureSource = new ShapeFileFeatureSource(buildRtgParameter.SourceShapefilePathName); sourceFeatureSource.Open(); // reset the process status. this.processedRecordCount = 0; this.totalRecordCount = sourceFeatureSource.GetCount(); RtgRoutingSource.GenerateRoutableShapeFile(buildRtgParameter.SourceShapefilePathName, buildRtgParameter.RoutableShapeFilePathName, buildRtgParameter.OverrideRoutableFile ? OverwriteMode.Overwrite : OverwriteMode.DoNotOverwrite, buildRtgParameter.GeographyUnit, 2); sourceFeatureSource.Close(); } } public void StartBuildingRtgFile() { routableFeatureSource = new ShapeFileFeatureSource(buildRtgParameter.RoutableShapeFilePathName); routableFeatureSource.Open(); // reset the process status. this.processedRecordCount = 0; this.totalRecordCount = routableFeatureSource.GetCount(); RtgRoutingSource.GenerateRoutingData(buildRtgParameter.RtgFilePathName, buildRtgParameter.SourceShapefilePathName, buildRtgParameter.RoutableShapeFilePathName, buildRtgParameter.BuildRoutingDataMode, buildRtgParameter.GeographyUnit, DistanceUnit.Meter); routableFeatureSource.Close(); if (cancelBuilding == true) { File.Delete(Path.ChangeExtension(buildRtgParameter.RtgFilePathName, ".rtg")); File.Delete(Path.ChangeExtension(buildRtgParameter.RtgFilePathName, ".rtx")); } // clear up the temporary shape files extracted from sqlite. if (File.Exists(buildRtgParameter.SourceSqlitePathName) && File.Exists(buildRtgParameter.SourceShapefilePathName)) { string tempShapefile = Path.GetFileNameWithoutExtension(buildRtgParameter.SourceShapefilePathName); string[] files = Directory.GetFiles(Path.GetDirectoryName(buildRtgParameter.SourceShapefilePathName)); foreach (string file in files) { if (Path.GetFileNameWithoutExtension(file).Equals(tempShapefile, StringComparison.InvariantCultureIgnoreCase)) { File.Delete(file); } } } OnBuildingIndexFinished(new EventArgs()); } public void CancelBuildingRtgFile() { cancelBuilding = true; } private void ShapeFileFeatureSource_BuildingIndex(object sender, BuildingIndexShapeFileFeatureSourceEventArgs e) { var handler = BuildingShapeFileIndex; if (handler != null) { handler(this, new RoutingIndexBuilderEventArgs(e.RecordCount, e.CurrentRecordIndex)); } } private void RtgRoutingSource_GeneratingRoutableShapeFile(object sender, GeneratingRoutableShapeFileRoutingSourceEventArgs e) { if (previousFeature == null) { previousFeature = e.PreviousFeature; } // we add the previous feature is because this event may be triggered multi times in one feature. if (previousFeature.Id != e.PreviousFeature.Id) { previousFeature = e.PreviousFeature; this.processedRecordCount++; OnBuildingRoutableSegment(new RoutingIndexBuilderEventArgs(this.totalRecordCount, this.processedRecordCount)); } } private void RtgRoutingSource_BuildingRoutingData(object sender, BuildingRoutingDataRtgRoutingSourceEventArgs e) { // Make progressBar move forward a step this.processedRecordCount++; OnBuildingRouteRTSegment(new RoutingIndexBuilderEventArgs(this.totalRecordCount, this.processedRecordCount)); // Get the processing Feature if (requiredColumns == null) { requiredColumns = GetRequiredColumns(); } Feature feature = routableFeatureSource.GetFeatureById(e.RouteSegment.FeatureId, requiredColumns); if (buildRtgParameter.IncludeOnewayOptions) { ProcessOneWayRoad(e, feature, requiredColumns); } if (buildRtgParameter.RouteIndexType == RoutingIndexType.Fastest) { if (buildRtgParameter.SpeedOption.SpeedSourceType == RoadSpeedSourceType.BasedOnRoadType && !string.IsNullOrEmpty(buildRtgParameter.SpeedOption.RoadTypeColumnName)) { string featureRoadClassValue = feature.ColumnValues[buildRtgParameter.SpeedOption.RoadTypeColumnName]; e.RouteSegment.Weight = GetDistance(e.RouteSegment.Distance) / GetSpeed(featureRoadClassValue); } else if (buildRtgParameter.SpeedOption.SpeedSourceType == RoadSpeedSourceType.DefinedInAttribution && !string.IsNullOrEmpty(buildRtgParameter.SpeedOption.SpeedColumnName)) { string speedString = feature.ColumnValues[buildRtgParameter.SpeedOption.SpeedColumnName]; float speed = float.TryParse(speedString, out speed) ? speed : buildRtgParameter.SpeedOption.DefaultSpeed; e.RouteSegment.Weight = GetDistance(e.RouteSegment.Distance) / speed; } else { e.RouteSegment.Weight = GetDistance(e.RouteSegment.Distance) / buildRtgParameter.SpeedOption.DefaultSpeed; } } e.Cancel = cancelBuilding; } private Collection GetRequiredColumns() { Collection requiredColumns = new Collection(); if (buildRtgParameter.IncludeOnewayOptions) { if (!string.IsNullOrEmpty(buildRtgParameter.OneWayRoadOption.OneWayColumnName)) { requiredColumns.Add(buildRtgParameter.OneWayRoadOption.OneWayColumnName); } if (!string.IsNullOrEmpty(buildRtgParameter.OneWayRoadOption.IndicatorColumnName)) { requiredColumns.Add(buildRtgParameter.OneWayRoadOption.IndicatorColumnName); } } string speedColumn = string.Empty; switch (buildRtgParameter.SpeedOption.SpeedSourceType) { case RoadSpeedSourceType.BasedOnRoadType: speedColumn = buildRtgParameter.SpeedOption.RoadTypeColumnName; break; case RoadSpeedSourceType.DefinedInAttribution: speedColumn = buildRtgParameter.SpeedOption.SpeedColumnName; break; default: break; } if (!string.IsNullOrEmpty(speedColumn)) { requiredColumns.Add(speedColumn); } return requiredColumns; } private void ProcessOneWayRoad(BuildingRoutingDataRtgRoutingSourceEventArgs e, Feature roadFeature, Collection oneWayColumns) { LineShape lineShape = GetLineShape(roadFeature); // handled feature is one-way road if (buildRtgParameter.OneWayRoadOption.MatchOneWayIdentifier(roadFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.OneWayColumnName])) { if (buildRtgParameter.OneWayRoadOption.MatchStartToEnd(roadFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName])) { e.RouteSegment.StartPointAdjacentIds.Clear(); } else if (buildRtgParameter.OneWayRoadOption.MatchEndToStart(roadFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName])) { e.RouteSegment.EndPointAdjacentIds.Clear(); } else if (buildRtgParameter.ClosedRoadOption.MatchClosedRoadValue(roadFeature.ColumnValues[buildRtgParameter.ClosedRoadOption.ClosedColumnName])) { e.RouteSegment.StartPointAdjacentIds.Clear(); e.RouteSegment.EndPointAdjacentIds.Clear(); } } // analysis adjacent one-way road features Collection removedStartPointAdjacentIds = GetRemovedAdjacentIds(e.RouteSegment.StartPointAdjacentIds, new PointShape(lineShape.Vertices[0]), oneWayColumns); foreach (string id in removedStartPointAdjacentIds) { e.RouteSegment.StartPointAdjacentIds.Remove(id); } Collection removedEndPointAdjacentIds = GetRemovedAdjacentIds(e.RouteSegment.EndPointAdjacentIds, new PointShape(lineShape.Vertices[lineShape.Vertices.Count - 1]), oneWayColumns); foreach (string id in removedEndPointAdjacentIds) { e.RouteSegment.EndPointAdjacentIds.Remove(id); } } private Collection GetRemovedAdjacentIds(Collection adjacentIds, PointShape intersectingPoint, Collection oneWayColumns) { Collection removedIds = new Collection(); foreach (string id in adjacentIds) { Feature adjacentFeature = routableFeatureSource.GetFeatureById(id, oneWayColumns); if (buildRtgParameter.OneWayRoadOption.MatchOneWayIdentifier(adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.OneWayColumnName])) //if (adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.OneWayColumnName].Equals(buildRtgParameter.OneWayRoadOption.OneWayIdentifier, StringComparison.InvariantCultureIgnoreCase)) { LineShape adjacentLineShape = GetLineShape(adjacentFeature); double distanceFromAdjacentStartToIntersecting = new PointShape(adjacentLineShape.Vertices[0]).GetDistanceTo(intersectingPoint, buildRtgParameter.GeographyUnit, DistanceUnit.Meter); double distanceFromAdjacentEndToIntersecting = new PointShape(adjacentLineShape.Vertices[adjacentLineShape.Vertices.Count - 1]).GetDistanceTo(intersectingPoint, buildRtgParameter.GeographyUnit, DistanceUnit.Meter); if (distanceFromAdjacentStartToIntersecting <= distanceFromAdjacentEndToIntersecting && buildRtgParameter.OneWayRoadOption.MatchEndToStart(adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName])) //if (distanceFromAdjacentStartToIntersecting <= distanceFromAdjacentEndToIntersecting && adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName].Equals(buildRtgParameter.OneWayRoadOption.EndToStart)) { removedIds.Add(id); } else if (distanceFromAdjacentEndToIntersecting <= distanceFromAdjacentStartToIntersecting && buildRtgParameter.OneWayRoadOption.MatchStartToEnd(adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName])) //&& adjacentFeature.ColumnValues[buildRtgParameter.OneWayRoadOption.IndicatorColumnName].Equals(buildRtgParameter.OneWayRoadOption.StartToEnd)) { removedIds.Add(id); } else if (buildRtgParameter.ClosedRoadOption.ClosedColumnName != string.Empty && buildRtgParameter.ClosedRoadOption.MatchClosedRoadValue(adjacentFeature.ColumnValues[buildRtgParameter.ClosedRoadOption.ClosedColumnName])) //else if (buildRtgParameter.ClosedRoadOption.ClosedColumnName != string.Empty && adjacentFeature.ColumnValues[buildRtgParameter.ClosedRoadOption.ClosedColumnName].Equals(buildRtgParameter.ClosedRoadOption.ClosedRoadValue)) { removedIds.Add(id); } } } return removedIds; } private float GetSpeed(String featureRoadSpeedClassValue) { float result = buildRtgParameter.SpeedOption.DefaultSpeed; foreach (var item in buildRtgParameter.SpeedOption.RoadSpeeds) { if (item.Key.Equals(featureRoadSpeedClassValue, StringComparison.OrdinalIgnoreCase)) { result = (float)item.Value; break; } } return result; } private float GetDistance(float distanceMeter) { switch (buildRtgParameter.SpeedOption.SpeedUnit) { case SpeedUnit.KilometersPerHour: return (float)(distanceMeter * 0.001); case SpeedUnit.MilesPerHour: return (float)(distanceMeter * 0.00062137); } return distanceMeter; } private static LineShape GetLineShape(Feature lineFeature) { BaseShape baseShape = lineFeature.GetShape(); LineShape lineShape = baseShape as LineShape; if (lineShape == null) { MultilineShape lineShapes = ((MultilineShape)baseShape); Collection vertices = new Collection(); foreach (LineShape line in lineShapes.Lines) { for (int i = 0; i < line.Vertices.Count; i++) { vertices.Add(line.Vertices[i]); } } lineShape = new LineShape(vertices); lineShape.Id = baseShape.Id; lineShape.Tag = baseShape.Tag; } return lineShape; } protected virtual void OnBuildingRouteRTSegment(RoutingIndexBuilderEventArgs e) { var handler = BuildingRouteRTSegment; if (handler != null) { handler(this, e); } } protected virtual void OnBuildingRoutableSegment(RoutingIndexBuilderEventArgs e) { var handler = BuildingRoutableSegment; if (handler != null) { handler(this, e); } } protected virtual void OnExtractingSqliteDatabase(RoutingIndexBuilderEventArgs e) { var handler = ExtractingSqliteDatabase; if (handler != null) { handler(this, e); } } protected virtual void OnBuildingIndexFinished(EventArgs e) { var handler = BuildingIndexFinished; if (handler != null) { handler(this, e); } } } } ====Program.cs==== using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms; namespace RoutingIndexGenerator { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new RoutingIndexGenerator()); } } } ====SpeedUnit.cs==== namespace RoutingIndexGenerator { public enum SpeedUnit { KilometersPerHour = 0, MilesPerHour = 1 } } ====RoutingIndexType.cs==== namespace RoutingIndexGenerator { public enum RoutingIndexType { Fastest = 0, Shortest = 1 } } ====RoadSpeedOption.cs==== using System.Collections.Generic; namespace RoutingIndexGenerator { public class RoadSpeedOption { private SpeedUnit speedUnit; private float defaultSpeed; private RoadSpeedSourceType roadSpeedSourceType; private string roadSpeedColumnName; private string roadTypeColumnName; private Dictionary roadSpeeds; public RoadSpeedOption() { DefaultSpeed = 50; SpeedUnit = SpeedUnit.KilometersPerHour; } public SpeedUnit SpeedUnit { get { return speedUnit; } set { speedUnit = value; } } public float DefaultSpeed { get { return defaultSpeed; } set { defaultSpeed = value; } } public RoadSpeedSourceType SpeedSourceType { get { return roadSpeedSourceType; } set { roadSpeedSourceType = value; } } public string SpeedColumnName { get { return roadSpeedColumnName; } set { roadSpeedColumnName = value; } } public string RoadTypeColumnName { get { return roadTypeColumnName; } set { roadTypeColumnName = value; } } public Dictionary RoadSpeeds { get { if (roadSpeeds == null) { roadSpeeds = new Dictionary(); } return roadSpeeds; } } } } ====OneWayRoadOption.cs==== using System; using System.Text.RegularExpressions; namespace RoutingIndexGenerator { public class OneWayRoadOption { private string oneWayColumnName; private string oneWayIdentifier; private string indicatorColumnName; private string startToEnd; private string endToStart; private Regex startToEndRegex; private Regex endToStartRegex; private Regex oneWayIdentifierRegex; public OneWayRoadOption() { } public string OneWayColumnName { get { return oneWayColumnName; } set { oneWayColumnName = value; } } public string IndicatorColumnName { get { return indicatorColumnName; } set { indicatorColumnName = value; } } public string OneWayIdentifier { get { return oneWayIdentifier; } set { oneWayIdentifier = value; } } public string StartToEnd { get { return startToEnd; } set { startToEnd = value; } } public string EndToStart { get { return endToStart; } set { endToStart = value; } } private Regex GetRegex(string stringToMatch) { if (String.IsNullOrEmpty(stringToMatch)) { return null; } Regex regex = null; if (stringToMatch.StartsWith("[") && stringToMatch.EndsWith("]")) { string regexString = stringToMatch.Substring(1, stringToMatch.Length - 2); regex = new Regex(regexString, RegexOptions.Compiled); } return regex; } public void InitRegexes() { this.endToStartRegex = GetRegex(this.endToStart); this.startToEndRegex = GetRegex(this.startToEnd); this.oneWayIdentifierRegex = GetRegex(this.oneWayIdentifier); } public bool MatchEndToStart(string endToStart) { bool match = false; if (endToStartRegex == null) { match = endToStart.Equals(this.endToStart, StringComparison.InvariantCultureIgnoreCase); } else { match = endToStartRegex.Match(endToStart).Success; } return match; } public bool MatchStartToEnd(string startToEnd) { bool match = false; if (startToEndRegex == null) { match = startToEnd.Equals(this.startToEnd, StringComparison.InvariantCultureIgnoreCase); } else { match = startToEndRegex.Match(startToEnd).Success; } return match; } public bool MatchOneWayIdentifier(string oneWayIdentifier) { bool match = false; if (oneWayIdentifierRegex == null) { match = oneWayIdentifier.Equals(this.oneWayIdentifier, StringComparison.InvariantCultureIgnoreCase); } else { match = oneWayIdentifierRegex.Match(oneWayIdentifier).Success; } return match; } } } ====RoutingIndexBuilderParameters.cs==== using System.IO; using ThinkGeo.MapSuite.Core; using ThinkGeo.MapSuite.Routing; namespace RoutingIndexGenerator { public class RoutingIndexBuilderParameters { private BuildRoutingDataMode buildRoutingDataMode; private GeographyUnit geographyUnit; private OneWayRoadOption oneWayRoadOption; private ClosedRoadOption closedRoadOption; private RoadSpeedOption speedOption; private RoutingIndexType routeIndexType; private RectangleShape restrictExtent; private string routableShapeFilePathName; private string rtgFilePathName; private string sourceSqlitePathName; private bool includeOnewayOptions; private bool overrideRoutableFile; public RoutingIndexBuilderParameters() { RouteIndexType = RoutingIndexType.Fastest; GeographyUnit = GeographyUnit.Meter; BuildRoutingDataMode = BuildRoutingDataMode.Rebuild; OneWayRoadOption = new OneWayRoadOption(); closedRoadOption = new ClosedRoadOption(); SpeedOption = new RoadSpeedOption(); overrideRoutableFile = true; } public RectangleShape RestrictExtent { get { return restrictExtent; } set { restrictExtent = value; } } public BuildRoutingDataMode BuildRoutingDataMode { get { return buildRoutingDataMode; } set { buildRoutingDataMode = value; } } public GeographyUnit GeographyUnit { get { return geographyUnit; } set { geographyUnit = value; } } public OneWayRoadOption OneWayRoadOption { get { return oneWayRoadOption; } set { oneWayRoadOption = value; } } public RoadSpeedOption SpeedOption { get { return speedOption; } set { speedOption = value; } } public ClosedRoadOption ClosedRoadOption { get { return closedRoadOption; } set { closedRoadOption = value; } } public RoutingIndexType RouteIndexType { get { return routeIndexType; } set { routeIndexType = value; } } public string RtgFilePathName { get { return rtgFilePathName; } set { rtgFilePathName = value; } } public string RoutableShapeFilePathName { get { return routableShapeFilePathName; } set { routableShapeFilePathName = value; } } public string SourceSqlitePathName { get { return sourceSqlitePathName; } set { sourceSqlitePathName = value; } } public string SourceShapefilePathName { get { return Path.ChangeExtension(sourceSqlitePathName, ".source.shp"); ; } } public bool IncludeOnewayOptions { get { return includeOnewayOptions; } set { includeOnewayOptions = value; } } public bool OverrideRoutableFile { get { return overrideRoutableFile; } set { overrideRoutableFile = value; } } } } ====RoutingIndexBuilderEventArgs.cs==== using System; namespace RoutingIndexGenerator { public class RoutingIndexBuilderEventArgs : EventArgs { private int totalRecordAccount; private int processedRecordAccount; public RoutingIndexBuilderEventArgs() { } public RoutingIndexBuilderEventArgs(int totalRecordAccount, int processedRecordAccount) { this.totalRecordAccount = totalRecordAccount; this.processedRecordAccount = processedRecordAccount; } public int TotalRecordCount { get { return totalRecordAccount; } set { totalRecordAccount = value; } } public int ProcessedRecordCount { get { return processedRecordAccount; } set { processedRecordAccount = value; } } } } ====ClosedRoadOptions.cs==== using System; using System.Text.RegularExpressions; namespace RoutingIndexGenerator { public class ClosedRoadOption { private string closedRoadValue; private string closedColumnName; private Regex closedRoadValueRegex; public string ClosedColumnName { get { return closedColumnName; } set { closedColumnName = value; } } public string ClosedRoadValue { get { return closedRoadValue; } set { closedRoadValue = value; } } private Regex GetRegex(string stringToMatch) { if (String.IsNullOrEmpty(stringToMatch)) { return null; } Regex regex = null; if (stringToMatch.StartsWith("[") && stringToMatch.EndsWith("]")) { string regexString = stringToMatch.Substring(1, stringToMatch.Length - 2); regex = new Regex(regexString, RegexOptions.Compiled); } return regex; } public void InitRegexes() { this.closedRoadValueRegex = GetRegex(this.closedRoadValue); } public bool MatchClosedRoadValue(string closedRoadValue) { bool match = false; if (closedRoadValue == null) { match = closedRoadValue.Equals(this.closedRoadValue, StringComparison.InvariantCultureIgnoreCase); } else { match = closedRoadValueRegex.Match(closedRoadValue).Success; } return match; } } }