Source Code RoutingEdition ProjectTemplates CityRoutingAndDirections Wpf CS.zip
MainWindow.xaml
<Window x:Class="ThinkGeo.MapSuite.RoutingExamples.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ThinkGeo.MapSuite.RoutingExamples"
xmlns:wpf="clr-namespace:ThinkGeo.MapSuite.WpfDesktopEdition;assembly=WpfDesktopEdition"
Title="City Routing and Directions"
Width="1155"
Height="675"
Icon="/CityRoutingAndDirections;Component/Image/MapSuite.ico"
Loaded="Window_Loaded"
WindowState="Maximized">
<Window.Resources>
<ResourceDictionary>
<local:CountToVisibilityConverter x:Key="CollectionToVisibilityConverter" />
<local:BooleanToReverseVisibilityConverter x:Key="BooleanToReverseVisibilityConverter" />
<local:PointShapeToStringConverter x:Key="PointShapeToStringConverter" />
<local:DistanceUnitToStringConverter x:Key="DistanceUnitToStringConverter" />
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</ResourceDictionary>
</Window.Resources>
<Grid Style="{StaticResource body}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Border x:Name="Border"
BorderThickness="0,0,0,5"
CornerRadius="2"
KeyboardNavigation.DirectionalNavigation="Contained"
KeyboardNavigation.TabIndex="2"
KeyboardNavigation.TabNavigation="Local"
Padding="10">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="#ffffff" />
<GradientStop Offset="0.4" Color="#fafafa" />
<GradientStop Offset="0.55" Color="#f2f2f2" />
<GradientStop Offset="0.6" Color="#f0f0f0" />
<GradientStop Offset="0.9" Color="#e2e2e2" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.85" Color="#5c707d" />
<GradientStop Offset="1" Color="#305c707d" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.BorderBrush>
<StackPanel Orientation="Horizontal">
<TextBlock Height="22"
VerticalAlignment="Bottom"
Style="{StaticResource h4}"
Text=" Map Suite" />
<TextBlock Style="{StaticResource h3}" Text=" City Routing and Directions" />
</StackPanel>
</Border>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="15" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid x:Name="gridDockPanel"
Grid.Column="0"
Margin="5 0 0 0"
HorizontalAlignment="Left"
Visibility="{Binding ElementName=btnCollpaseToggle, Path=IsChecked, Converter={StaticResource BooleanToReverseVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical">
<TextBlock Width="290"
Margin="0 10 0 5"
VerticalAlignment="Center"
Style="{StaticResource h4}"
Text="Right click on the map to set up a route:"
TextWrapping="Wrap" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0"
Grid.Column="0"
Margin="4 0 0 0"
Source="/CityRoutingAndDirections;Component/Image/start.png"
Style="{StaticResource msImage}" />
<TextBox x:Name="txtStart"
Grid.Row="0"
Grid.Column="1"
IsReadOnly="True"
Width="250"
Style="{StaticResource msTextbox}"
Text="{Binding StartPoint, Mode=TwoWay, Converter={StaticResource PointShapeToStringConverter}}" />
<Button Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="2"
Command="{Binding InverseDestinationCommand}"
Cursor="Hand"
ToolTip="Switch start and end search point"
Template="{StaticResource BorderlessButtonTemplate}"
Visibility="{Binding StopItems.Count, Converter={StaticResource CollectionToVisibilityConverter}}">
<Image Source="/CityRoutingAndDirections;Component/Image/switch.png" Style="{StaticResource msButtonImage}" />
</Button>
<Image Grid.Row="2"
Grid.Column="0"
Margin="4 0 0 0"
Source="/CityRoutingAndDirections;Component/Image/end.png"
Style="{StaticResource msImage}" />
<TextBox x:Name="txtEnd"
Grid.Row="2"
Grid.Column="1"
IsReadOnly="True"
Width="250"
Style="{StaticResource msTextbox}"
Text="{Binding EndPoint, Mode=TwoWay, Converter={StaticResource PointShapeToStringConverter}}" />
<ItemsControl x:Name="lisLocations"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="4 0 0 0"
BorderBrush="Transparent"
ItemsSource="{Binding StopItems}"
Padding="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0"
Source="{Binding MarkerImageSource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource msImage}" />
<TextBox Grid.Column="1"
Width="250"
IsReadOnly="True"
Style="{StaticResource msTextbox}"
Text="{Binding Location, Mode=TwoWay, Converter={StaticResource PointShapeToStringConverter}}" />
<Button Grid.Column="2"
Command="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, Path=DataContext.DeleteStopCommand}"
CommandParameter="{Binding .}"
Template="{StaticResource BorderlessButtonTemplate}">
<Image Source="/CityRoutingAndDirections;component/Image/remove.png" Style="{StaticResource msButtonImage}" />
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<TextBlock Style="{StaticResource h4}" Text="Tools:" />
<Border Margin="0 5 0 5"
BorderBrush="LightGray"
BorderThickness="1"
CornerRadius="5">
<StackPanel Margin="5" Orientation="Horizontal">
<local:ImageRadioButton x:Name="rbtnPan"
CheckImageSource="/CityRoutingAndDirections;component/Image/pan.png"
Command="{Binding UpdateTrackModeCommand}"
CommandParameter="None"
ToolTip="Pan the map"
IsChecked="True"
Style="{StaticResource msRadioButton}" />
<local:ImageRadioButton x:Name="rbtnPolygon"
Margin="10 0 0 0"
CheckImageSource="/CityRoutingAndDirections;component/Image/draw_polygon.png"
Command="{Binding UpdateTrackModeCommand}"
CommandParameter="Polygon"
ToolTip="Draw areas to avoid traveling through"
Style="{StaticResource msRadioButton}" />
<Button Margin="10 0 0 0"
Command="{Binding UndoCommand}"
ToolTip="Undo last area drawn"
Template="{StaticResource BorderlessButtonTemplate}">
<Image Source="/CityRoutingAndDirections;component/Image/undo.png" Style="{StaticResource msButtonImage}" />
</Button>
<Button Margin="10 0 0 0"
Command="{Binding ClearBarriersCommand}"
ToolTip="Delete all areas"
Template="{StaticResource BorderlessButtonTemplate}">
<Image Source="/CityRoutingAndDirections;component/Image/clear.png" Style="{StaticResource msButtonImage}" />
</Button>
</StackPanel>
</Border>
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<Button Command="{Binding CalculateDirectionCommand}"
Content="Get Directions"
Style="{StaticResource msButton}" />
<Button Margin="0 5 0 5"
Command="{Binding ClearAllCommand}"
Content="Reset Route"
Style="{StaticResource msButton}" />
</StackPanel>
<Border Style="{StaticResource headerBanner}">
<TextBlock Style="{StaticResource headerText}"
Text="Result" />
</Border>
<Grid Margin="0 0 0 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0"
Grid.Column="0"
Margin="5 0 0 0"
VerticalAlignment="Center"
Orientation="Horizontal">
<RadioButton x:Name="rbtnShortest"
Command="{Binding CalculateDirectionCommand}"
CommandParameter="Shortest"
GroupName="rbtnRouteMode">
Shortest
</RadioButton>
<RadioButton x:Name="rbtnFastest"
Margin="5 0 0 0"
Command="{Binding CalculateDirectionCommand}"
CommandParameter="Fastest"
GroupName="rbtnRouteMode"
IsChecked="True">
Fastest
</RadioButton>
</StackPanel>
<ComboBox x:Name="cblDistanceUnt"
Grid.Row="0"
Grid.Column="1"
Margin="15 0 0 0"
SelectedItem="{Binding DistanceUnit,Mode=TwoWay}"
ItemsSource="{Binding DistanceUnitCandidates}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding .,Converter={StaticResource DistanceUnitToStringConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
<DataGrid Grid.Row="1"
Width="300"
Margin="0 0 0 5"
AutoGenerateColumns="False"
CanUserSortColumns="False"
ItemsSource="{Binding DirectionResultList}">
<DataGrid.Resources>
<local:DirectionTypeToImageSourceConverter x:Key="DirectionTypeToImageSourceConverter" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn CanUserResize="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Width="20"
Height="20"
Source="{Binding Direction, Converter={StaticResource DirectionTypeToImageSourceConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Width="160"
Binding="{Binding RoadName}"
Header="Road Name"
IsReadOnly="True" />
<DataGridTextColumn Width="*"
Binding="{Binding Length}"
Header="Length"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
</Grid>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<ToggleButton x:Name="btnCollpaseToggle"
VerticalAlignment="Center"
Background="Transparent"
BorderBrush="Transparent"
Cursor="Hand">
<ToggleButton.Style>
<Style>
<Style.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="False">
<Setter Property="ToggleButton.Content">
<Setter.Value>
<Image Source="/CityRoutingAndDirections;Component/Image/collapse.gif" Stretch="None" />
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="ToggleButton.Content">
<Setter.Value>
<Image Source="/CityRoutingAndDirections;Component/Image/expand.gif" Stretch="None" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<Rectangle Width="5">
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="0" Color="#5c707d" />
<GradientStop Offset="1" Color="#305c707d" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</StackPanel>
<wpf:WpfMap x:Name="wpfMap1"
Grid.Column="2"
Panel.ZIndex="-1">
<wpf:WpfMap.ContextMenu>
<ContextMenu x:Name="menuRoute">
<MenuItem x:Name="menuiStart"
Command="{Binding AddPointCommand}"
CommandParameter="Start">
<MenuItem.Header>
<TextBlock VerticalAlignment="Center" Text="Directions from here" />
</MenuItem.Header>
<MenuItem.Icon>
<Image Height="20" Source="/CityRoutingAndDirections;component/Image/start.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuiEnd"
Command="{Binding AddPointCommand}"
CommandParameter="End">
<MenuItem.Header>
<TextBlock VerticalAlignment="Center" Text="Directions to here" />
</MenuItem.Header>
<MenuItem.Icon>
<Image Height="20" Source="/CityRoutingAndDirections;component/Image/end.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuiStop"
Command="{Binding AddPointCommand}"
CommandParameter="Stop">
<MenuItem.Header>
<TextBlock VerticalAlignment="Center" Text="Add a waypoint here" />
</MenuItem.Header>
<MenuItem.Icon>
<Image Height="20" Source="/CityRoutingAndDirections;component/Image/stop.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</wpf:WpfMap.ContextMenu>
</wpf:WpfMap>
<local:LoadingImageUserControl Grid.Column="2" Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
<Grid Grid.Row="2">
<Border BorderBrush="Gray" BorderThickness="0 1 0 0">
<StatusBar Height="30">
<StatusBarItem HorizontalContentAlignment="Stretch">
<Grid HorizontalAlignment="Right">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="tblCoordinateX"
Grid.Column="0"
Width="75"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding CoordinateX, StringFormat=X: {0:N6}}" />
<TextBlock x:Name="tblCoordinateY"
Grid.Column="2"
Width="75"
VerticalAlignment="Center"
Text="{Binding CoordinateY, StringFormat=Y: {0:N6}}" />
</Grid>
</StatusBarItem>
</StatusBar>
</Border>
</Grid>
</Grid>
</Window>
BooleanToReverseVisibilityConverter.cs
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class BooleanToReverseVisibilityConverter :IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool)
{
bool isVisible = (bool)value;
return isVisible ? Visibility.Collapsed : Visibility.Visible;
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
RouteDirectionModel.cs
using System;
using System.Globalization;
using ThinkGeo.MapSuite.Core;
using ThinkGeo.MapSuite.Routing;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class RouteDirectionViewModel : ViewModelBase
{
private string roadNameColumnName;
private Feature roadFeature;
private DistanceUnit lengthUnit;
private RouteSegment roadSegment;
private GeographyUnit geographyUnit;
public RouteDirectionViewModel()
: this(null, null, null)
{ }
public RouteDirectionViewModel(RouteSegment roadSegment, Feature roadFeature, string roadNameColumnName)
: this(roadSegment, roadFeature, roadNameColumnName, GeographyUnit.DecimalDegree, DistanceUnit.Meter)
{ }
public RouteDirectionViewModel(RouteSegment roadSegment, Feature roadFeature, string roadNameColumnName, GeographyUnit geographyUnit, DistanceUnit lengthUnit)
{
this.lengthUnit = lengthUnit;
this.roadSegment = roadSegment;
this.roadFeature = roadFeature;
this.geographyUnit = geographyUnit;
this.roadNameColumnName = roadNameColumnName;
}
public DrivingDirection Direction
{
get
{
return roadSegment != null ? roadSegment.DrivingDirection : DrivingDirection.Back;
}
}
public GeographyUnit GeographyUnit
{
get { return geographyUnit; }
set { geographyUnit = value; }
}
public string Length
{
get
{
string length = string.Empty;
if (RoadFeature != null)
{
double value = Math.Round(((LineBaseShape)RoadFeature.GetShape()).GetLength(GeographyUnit, lengthUnit), 2);
length = String.Format(CultureInfo.InvariantCulture, "{0} {1}", value, lengthUnit == DistanceUnit.Mile ? "mi" : "km");
}
return length;
}
}
public DistanceUnit LengthUnit
{
get { return lengthUnit; }
set
{
lengthUnit = value;
RaisePropertyChanged(() => LengthUnit);
RaisePropertyChanged(() => Length);
}
}
public Feature RoadFeature
{
get { return roadFeature; }
set { roadFeature = value; }
}
public string RoadName
{
get
{
return !string.IsNullOrEmpty(RoadNameColumnName) ? roadFeature.ColumnValues[RoadNameColumnName] : string.Empty;
}
}
public string RoadNameColumnName
{
get { return roadNameColumnName; }
set { roadNameColumnName = value; }
}
}
}
CommandBase.cs
using System;
using System.Diagnostics;
using System.Windows.Input;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class CommandBase : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public CommandBase(Action<object> execute)
: this(execute, null)
{
}
public CommandBase(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
JsonSerializer.cs
using System;
using System.Windows;
using System.Windows.Data;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class CountToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility result = Visibility.Visible;
int count = (int)value;
if (count > 0) result = Visibility.Hidden;
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
DirectionTypeToImageSourceConverter.cs
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media.Imaging;
using ThinkGeo.MapSuite.Routing;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class DirectionTypeToImageSourceConverter : IValueConverter
{
private static string directionImageUriTemplate = "/CityRoutingAndDirections;component/Image/Directions/{0}";
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is DrivingDirection)
{
DrivingDirection direction = (DrivingDirection)value;
string directionImageUriString;
switch (direction)
{
case DrivingDirection.Back:
directionImageUriString = "back.png";
break;
case DrivingDirection.Forward:
directionImageUriString = "Forward.png";
break;
case DrivingDirection.Invalid:
directionImageUriString = "Invalid.png";
break;
case DrivingDirection.LeftForward:
directionImageUriString = "LeftForward.png";
break;
case DrivingDirection.RightBack:
directionImageUriString = "RightBack.png";
break;
case DrivingDirection.RightForward:
directionImageUriString = "RightForward.png";
break;
case DrivingDirection.LeftBack:
directionImageUriString = "LeftBack.png";
break;
case DrivingDirection.Left:
directionImageUriString = "Left.png";
break;
default:
directionImageUriString = "Right.png";
break;
}
string url = string.Format(CultureInfo.InvariantCulture, directionImageUriTemplate, directionImageUriString);
return new BitmapImage(new Uri(url, UriKind.RelativeOrAbsolute));
}
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}
PointShapeToStringConverter.cs
using System;
using System.Globalization;
using System.Windows.Data;
using ThinkGeo.MapSuite.Core;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class PointShapeToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
object returnValue = string.Empty;
PointShape point = value as PointShape;
if (point != null)
{
returnValue = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", point.X.ToString("N8"), point.Y.ToString("N8"));
}
return returnValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
object returnValue = null;
string pointText = value as string;
if (!string.IsNullOrEmpty(pointText))
{
if (pointText != null)
{
string[] segements = pointText.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
double x, y;
if (segements.Length == 2 && double.TryParse(segements[0], out x) &&
double.TryParse(segements[1], out y))
{
returnValue = new PointShape(x, y);
}
}
}
return returnValue;
}
}
}
MainWindowViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
using ThinkGeo.MapSuite.Core;
using ThinkGeo.MapSuite.Routing;
using ThinkGeo.MapSuite.WpfDesktopEdition;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class MainWindowViewModel : ViewModelBase
{
private static readonly string roadColumnName = "FULL_STREE";
private static readonly string austinCountyShapeFile = @"..\..\App_Data\AustinBorder.shp";
private static readonly string austinStreetShapeFile = @"..\..\App_Data\austin_streets.shp";
private static readonly string rtgFastestFile = @"..\..\App_Data\austin_streets_fastest.rtg";
private static readonly string rtgShortestFile = @"..\..\App_Data\austin_streets_shortest.rtg";
private static readonly RectangleShape defaultExtent = new RectangleShape(-97.803553154785, 30.352039937378, -97.700175654785, 30.259261310914);
private bool isBusy;
private double coordinateX;
private double coordinateY;
private MultipolygonShape restrictedPolygon;
private WpfMap mapControl;
private DistanceUnit distanceUnit;
private RoutingLayer routingLayer;
private RoutePriority routePriority;
private LayerOverlay routingOverlay;
private PointShape routeLocationPoint;
private InMemoryFeatureLayer barrierLayer;
private ShapeFileFeatureLayer restrictedLayer;
private ShapeFileFeatureLayer routingShapeFile;
private Collection<string> avoidableFeatureIds;
private Collection<DistanceUnit> distanceUnitCandidates;
private ObservableCollection<StopItemViewModel> stopItems;
private ObservableCollection<RouteDirectionViewModel> directionResultList;
private CommandBase undoCommand;
private CommandBase clearAllCommand;
private CommandBase addPointCommand;
private CommandBase deleteStopCommand;
private CommandBase clearBarriersCommand;
private CommandBase updateTrackModeCommand;
private CommandBase calculateDirectionCommand;
private CommandBase inverseDestinationCommand;
public MainWindowViewModel(WpfMap map)
{
mapControl = map;
distanceUnit = DistanceUnit.Mile;
PointShape defaultCenter = defaultExtent.GetCenterPoint();
coordinateX = defaultCenter.X;
coordinateY = defaultCenter.Y;
avoidableFeatureIds = new Collection<string>();
stopItems = new ObservableCollection<StopItemViewModel>();
stopItems.CollectionChanged += LocationItems_CollectionChanged;
InitializeMap();
}
public DistanceUnit DistanceUnit
{
get { return distanceUnit; }
set
{
distanceUnit = value;
RaisePropertyChanged(() => DistanceUnit);
if (mapControl != null)
{
ScaleBarAdornmentLayer adornmentLayer = (ScaleBarAdornmentLayer)mapControl.AdornmentOverlay.Layers["ScaleBar"];
adornmentLayer.UnitFamily = value == DistanceUnit.Kilometer ? UnitSystem.Metric : UnitSystem.Imperial;
}
foreach (RouteDirectionViewModel item in DirectionResultList)
{
item.LengthUnit = distanceUnit;
}
}
}
public Collection<DistanceUnit> DistanceUnitCandidates
{
get { return distanceUnitCandidates ?? (distanceUnitCandidates = new Collection<DistanceUnit> { DistanceUnit.Mile, DistanceUnit.Kilometer }); }
}
public bool IsBusy
{
get { return isBusy; }
set
{
isBusy = value;
RaisePropertyChanged(() => IsBusy);
}
}
public WpfMap MapControl
{
get { return mapControl; }
set { mapControl = value; }
}
public double CoordinateX
{
get { return coordinateX; }
set
{
coordinateX = value;
RaisePropertyChanged(() => CoordinateX);
}
}
public double CoordinateY
{
get { return coordinateY; }
set
{
coordinateY = value;
RaisePropertyChanged(() => CoordinateY);
}
}
public PointShape StartPoint
{
get { return routingLayer.StartPoint; }
set
{
if (routingLayer.StartPoint != value)
{
routingLayer.StartPoint = value;
RaisePropertyChanged(() => StartPoint);
routingOverlay.Refresh();
}
}
}
public PointShape EndPoint
{
get { return routingLayer.EndPoint; }
set
{
if (routingLayer.EndPoint != value)
{
routingLayer.EndPoint = value;
RaisePropertyChanged(() => EndPoint);
routingOverlay.Refresh();
}
}
}
public ObservableCollection<RouteDirectionViewModel> DirectionResultList
{
get
{
return directionResultList ?? (directionResultList = new ObservableCollection<RouteDirectionViewModel>());
}
}
public ObservableCollection<StopItemViewModel> StopItems
{
get { return stopItems; }
}
public CommandBase UndoCommand
{
get
{
return undoCommand ?? (undoCommand = new CommandBase(obj =>
{
if (barrierLayer.InternalFeatures.Count == 0) return;
barrierLayer.InternalFeatures.RemoveAt(barrierLayer.InternalFeatures.Count - 1);
routingShapeFile.Open();
avoidableFeatureIds.Clear();
foreach (Feature feature in barrierLayer.InternalFeatures)
{
Collection<Feature> features = routingShapeFile.FeatureSource.GetFeaturesWithinDistanceOf(feature, mapControl.MapUnit, DistanceUnit.Meter, 1, ReturningColumnsType.NoColumns);
foreach (Feature avoidableFeature in features)
{
avoidableFeatureIds.Add(avoidableFeature.Id);
}
}
routingOverlay.Refresh();
}));
}
}
public CommandBase AddPointCommand
{
get
{
return addPointCommand ?? (addPointCommand = new CommandBase(obj =>
{
restrictedLayer.Open();
Collection<Feature> features = restrictedLayer.FeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns);
if (features.Count > 0 && !features[0].GetShape().Contains(routeLocationPoint))
{
Window warningWindow = new CityRoutingWarningWindow();
warningWindow.Owner = Application.Current.MainWindow;
warningWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
warningWindow.ShowDialog();
return;
}
string commandParameter = obj as string;
if (commandParameter != null)
{
switch (commandParameter)
{
case "Start":
StartPoint = routeLocationPoint;
break;
case "End":
EndPoint = routeLocationPoint;
break;
case "Stop":
routingLayer.StopPoints.Add(routeLocationPoint);
StopItemViewModel locationItem = new StopItemViewModel(routeLocationPoint);
locationItem.PropertyChanged += SyncStopsToRouteLayer;
StopItems.Add(locationItem);
break;
}
DirectionResultList.Clear();
routingLayer.Routes.Clear();
}
routingOverlay.Refresh();
}));
}
}
public CommandBase ClearAllCommand
{
get
{
return clearAllCommand ?? (clearAllCommand = new CommandBase(obj =>
{
EndPoint = null;
StartPoint = null;
StopItems.Clear();
DirectionResultList.Clear();
routingLayer.StopPoints.Clear();
routingLayer.Routes.Clear();
routingOverlay.Refresh();
}));
}
}
public CommandBase DeleteStopCommand
{
get
{
return deleteStopCommand ?? (deleteStopCommand = new CommandBase(obj =>
{
StopItemViewModel item = (StopItemViewModel)obj;
StopItems.Remove(item);
routingLayer.StopPoints.Remove(item.Location);
routingLayer.Routes.Clear();
DirectionResultList.Clear();
routingOverlay.Refresh();
}));
}
}
public CommandBase ClearBarriersCommand
{
get
{
return clearBarriersCommand ?? (clearBarriersCommand = new CommandBase(obj =>
{
barrierLayer.InternalFeatures.Clear();
avoidableFeatureIds.Clear();
routingOverlay.Refresh();
}));
}
}
public CommandBase UpdateTrackModeCommand
{
get
{
return updateTrackModeCommand ?? (updateTrackModeCommand = new CommandBase(obj =>
{
string commandParameter = obj as string;
if (!string.IsNullOrEmpty(commandParameter))
{
mapControl.TrackOverlay.TrackMode = (TrackMode)Enum.Parse(typeof(TrackMode), commandParameter);
}
}));
}
}
public CommandBase CalculateDirectionCommand
{
get
{
return calculateDirectionCommand ?? (calculateDirectionCommand = new CommandBase(obj =>
{
if (routingLayer == null || routingLayer.StartPoint == null || routingLayer.EndPoint == null) return;
IsBusy = true;
string commandParameter = obj as string;
if (!string.IsNullOrEmpty(commandParameter))
{
routePriority = (RoutePriority)Enum.Parse(typeof(RoutePriority), commandParameter);
}
string rtgFilePathName = string.Empty;
switch (routePriority)
{
case RoutePriority.Fastest:
rtgFilePathName = rtgFastestFile;
break;
case RoutePriority.Shortest:
rtgFilePathName = rtgShortestFile;
break;
}
Task.Factory.StartNew(() =>
{
RtgRoutingSource routingSource = new RtgRoutingSource(rtgFilePathName);
RoutingEngine routingEngine = new RoutingEngine(routingSource, routingShapeFile.FeatureSource);
RoutePathAndDirection routePathAndDirection = new RoutePathAndDirection(routingEngine, routingLayer.StartPoint, routingLayer.EndPoint, barrierLayer.FeatureSource, avoidableFeatureIds, routingLayer.StopPoints);
RoutingResult result = routePathAndDirection.GetRoute();
routingLayer.Routes.Clear();
routingLayer.Routes.Add(result.Route);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
DirectionResultList.Clear();
for (int i = 0; i < result.RouteSegments.Count; i++)
{
RouteDirectionViewModel routeDirection = new RouteDirectionViewModel(result.RouteSegments[i], result.Features[i], roadColumnName, mapControl.MapUnit, distanceUnit);
DirectionResultList.Add(routeDirection);
}
routingOverlay.Refresh();
IsBusy = false;
}));
});
}));
}
}
public CommandBase InverseDestinationCommand
{
get
{
return inverseDestinationCommand ?? (inverseDestinationCommand = new CommandBase(obj =>
{
PointShape end = StartPoint;
StartPoint = EndPoint;
EndPoint = end;
routingOverlay.Refresh();
}));
}
}
private void InitializeMap()
{
routingShapeFile = new ShapeFileFeatureLayer(austinStreetShapeFile);
mapControl.MapUnit = GeographyUnit.DecimalDegree;
mapControl.CurrentExtent = defaultExtent;
mapControl.MapTools.MouseCoordinate.IsEnabled = true;
mapControl.MapTools.MouseCoordinate.MouseCoordinateType = MouseCoordinateType.Custom;
mapControl.MapTools.MouseCoordinate.CustomFormatted += MouseCoordinate_CustomFormatted;
mapControl.MapTools.PanZoomBar.GlobeButtonClick += (s, e) => e.NewExtent = defaultExtent;
mapControl.TrackOverlay.TrackEnded += TrackOverlay_TrackEnded;
mapControl.PreviewMouseRightButtonDown += MapControl_PreviewMouseRightButtonDown;
//Init Backgound Layer
mapControl.Overlays.Add("WorldMapKit", new WorldMapKitWmsWpfOverlay());
//Init RoutingOverlay
routingOverlay = new LayerOverlay();
mapControl.Overlays.Add("RoutingOverlay", routingOverlay);
routingLayer = new RoutingLayer();
routingLayer.StartPointStyle = new PointStyle(GetMarkerImage("start"));
routingLayer.EndPointStyle = new PointStyle(GetMarkerImage("end"));
routingLayer.StopPointStyle = new PointStyle(GetMarkerImage("stop"));
routingLayer.StopPointStyle.YOffsetInPixel = -10;
routingLayer.StopTextStyle.TextSolidBrush.Color = GeoColor.StandardColors.White;
routingLayer.StopTextStyle.Font = new GeoFont("Arial", 10);
routingLayer.XOffsetInPixelOfStopOrder = -3;
routingLayer.YOffsetInPixelOfStopOrder = 14;
routingOverlay.Layers.Add("RoutingLayer", routingLayer);
barrierLayer = new InMemoryFeatureLayer();
barrierLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = AreaStyles.CreateSimpleAreaStyle(GeoColor.FromArgb(100, GeoColor.SimpleColors.Red), GeoColor.SimpleColors.Red, 1);
barrierLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
routingOverlay.Layers.Add("BarrierLayer", barrierLayer);
//Init restrictedOverlay
LayerOverlay restrictedOverlay = new LayerOverlay();
mapControl.Overlays.Add("RestrictedOverlay", restrictedOverlay);
restrictedLayer = new ShapeFileFeatureLayer(austinCountyShapeFile);
restrictedLayer.Open();
restrictedPolygon = (MultipolygonShape)restrictedLayer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns)[0].GetShape();
AreaStyle extentStyle = new AreaStyle();
extentStyle.CustomAreaStyles.Add(new AreaStyle(new GeoSolidBrush(GeoColor.SimpleColors.Transparent)) { OutlinePen = new GeoPen(GeoColor.SimpleColors.White, 9.5f) });
extentStyle.CustomAreaStyles.Add(new AreaStyle(new GeoSolidBrush(GeoColor.SimpleColors.Transparent)) { OutlinePen = new GeoPen(GeoColor.SimpleColors.Red, 1.5f) { DashStyle = LineDashStyle.Dash } });
restrictedLayer.ZoomLevelSet.ZoomLevel01.DefaultAreaStyle = extentStyle;
restrictedLayer.ZoomLevelSet.ZoomLevel01.ApplyUntilZoomLevel = ApplyUntilZoomLevel.Level20;
restrictedOverlay.Layers.Add("RestrictedLayer", restrictedLayer);
// Scale bar layer
ScaleBarAdornmentLayer scaleBarAdormentLayer = new ScaleBarAdornmentLayer();
scaleBarAdormentLayer.Location = AdornmentLocation.LowerLeft;
scaleBarAdormentLayer.XOffsetInPixel = 10;
mapControl.AdornmentOverlay.Layers.Add("ScaleBar", scaleBarAdormentLayer);
mapControl.Refresh();
}
private void SyncStopsToRouteLayer(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName.Equals("Location"))
{
routingLayer.StopPoints.Clear();
foreach (var item in StopItems)
{
routingLayer.StopPoints.Add(item.Location);
}
}
}
private void LocationItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
for (int i = 0; i < stopItems.Count; i++)
{
StopItems[i].MarkerImageSource = GetMarkerImageSource(i + 1);
}
}
private void MouseCoordinate_CustomFormatted(object sender, CustomFormattedMouseCoordinateMapToolEventArgs e)
{
CoordinateX = e.WorldCoordinate.X;
CoordinateY = e.WorldCoordinate.Y;
}
private void TrackOverlay_TrackEnded(object sender, TrackEndedTrackInteractiveOverlayEventArgs e)
{
if (mapControl.TrackOverlay.TrackShapeLayer.InternalFeatures.Count > 0)
{
PolygonShape trackedPolygonShape = mapControl.TrackOverlay.TrackShapeLayer.InternalFeatures[0].GetShape() as PolygonShape;
if (trackedPolygonShape != null && trackedPolygonShape.IsWithin(restrictedPolygon))
{
barrierLayer.InternalFeatures.Add(mapControl.TrackOverlay.TrackShapeLayer.InternalFeatures[0]);
}
else
{
MessageBox.Show("Please note that this sample map is only able to analyze service areas within the Austin city limits, which are indicated by a dashed red line. Click inside that boundary for best results.", "Draw Area");
}
mapControl.TrackOverlay.TrackShapeLayer.InternalFeatures.Clear();
}
routingShapeFile.Open();
avoidableFeatureIds.Clear();
foreach (Feature feature in barrierLayer.InternalFeatures)
{
Collection<Feature> features = routingShapeFile.FeatureSource.GetFeaturesWithinDistanceOf(feature, mapControl.MapUnit, DistanceUnit.Meter, 1, ReturningColumnsType.NoColumns);
foreach (Feature avoidableFeature in features.Where(f => !avoidableFeatureIds.Contains(f.Id)))
{
avoidableFeatureIds.Add(avoidableFeature.Id);
}
}
routingOverlay.Refresh();
}
private void MapControl_PreviewMouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Point menuRouteMouseLocation = e.GetPosition(mapControl);
ScreenPointF mouseScreenPoint = new ScreenPointF((float)menuRouteMouseLocation.X, (float)menuRouteMouseLocation.Y);
routeLocationPoint = ExtentHelper.ToWorldCoordinate(mapControl.CurrentExtent, mouseScreenPoint, (float)mapControl.ActualWidth, (float)mapControl.ActualHeight);
}
private static GeoImage GetMarkerImage(string imageName)
{
string url = string.Format(CultureInfo.InvariantCulture, "pack://application:,,,/CityRoutingAndDirections;Component/Image/{0}.png", imageName);
return new GeoImage(Application.GetResourceStream(new Uri(url, UriKind.RelativeOrAbsolute)).Stream);
}
private static BitmapImage GetMarkerImageSource(int index)
{
System.Drawing.Bitmap bitmap = null;
System.Drawing.Graphics g = null;
try
{
Uri stopImageUri = new Uri("pack://application:,,,/CityRoutingAndDirections;Component/Image/stop.png", UriKind.RelativeOrAbsolute);
bitmap = new System.Drawing.Bitmap(Application.GetResourceStream(stopImageUri).Stream);
g = System.Drawing.Graphics.FromImage(bitmap);
string currentIndex = index.ToString(CultureInfo.InvariantCulture);
System.Drawing.Font font = new System.Drawing.Font("Segoe UI", 10, System.Drawing.FontStyle.Bold);
System.Drawing.SizeF size = g.MeasureString(currentIndex, font);
g.DrawString(currentIndex, font, new System.Drawing.SolidBrush(System.Drawing.Color.White), new System.Drawing.PointF((bitmap.Width - size.Width) / 2, 0));
MemoryStream ms = new MemoryStream();
bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] bytes = ms.GetBuffer();
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(bytes);
bitmapImage.EndInit();
return bitmapImage;
}
finally
{
if (bitmap != null) bitmap.Dispose();
if (g != null) g.Dispose();
}
}
private enum RoutePriority
{
Shortest = 0,
Fastest = 1
}
}
}
RoutePathAndDirection.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using ThinkGeo.MapSuite.Core;
using ThinkGeo.MapSuite.Routing;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class RoutePathAndDirection
{
private readonly List<PointShape> stopPoints;
private readonly List<string> avoidableFeatureIds;
private PointShape endPoint;
private PointShape startPoint;
private RoutingEngine routingEngine;
private FeatureSource barrierFeatureSource;
public RoutePathAndDirection()
{ }
public RoutePathAndDirection(RoutingEngine routingEngine, PointShape startPoint, PointShape endPoint)
: this(routingEngine, startPoint, endPoint, null)
{ }
public RoutePathAndDirection(RoutingEngine routingEngine, PointShape startPoint, PointShape endPoint, FeatureSource barrierFeatureSource)
: this(routingEngine, startPoint, endPoint, barrierFeatureSource, new string[] { })
{ }
public RoutePathAndDirection(RoutingEngine routingEngine, PointShape startPoint, PointShape endPoint, FeatureSource barrierFeatureSource, IEnumerable<string> avoidableFeatureIds)
: this(routingEngine, startPoint, endPoint, barrierFeatureSource, avoidableFeatureIds, new PointShape[] { })
{ }
public RoutePathAndDirection(RoutingEngine routingEngine, PointShape startPoint, PointShape endPoint, FeatureSource barrierFeatureSource, IEnumerable<PointShape> stopPoints)
: this(routingEngine, startPoint, endPoint, barrierFeatureSource, new string[] { }, stopPoints)
{ }
public RoutePathAndDirection(RoutingEngine routingEngine, PointShape startPoint, PointShape endPoint, FeatureSource barrierFeatureSource, IEnumerable<string> avoidableFeatureIds, IEnumerable<PointShape> stopPoints)
{
this.endPoint = endPoint;
this.startPoint = startPoint;
this.routingEngine = routingEngine;
this.barrierFeatureSource = barrierFeatureSource;
this.stopPoints = new List<PointShape>(stopPoints);
this.avoidableFeatureIds = new List<string>(avoidableFeatureIds);
routingEngine.RoutingAlgorithm.FindingRoute += RoutingAlgorithm_FindingRoute;
}
public PointShape StartPoint
{
get { return startPoint; }
set { startPoint = value; }
}
public PointShape EndPoint
{
get { return endPoint; }
set { endPoint = value; }
}
public List<PointShape> StopPoints
{
get { return stopPoints; }
}
public RoutingEngine RoutingEngine
{
get { return routingEngine; }
set { routingEngine = value; }
}
public FeatureSource BarrierFeatureSource
{
get { return barrierFeatureSource; }
set { barrierFeatureSource = value; }
}
public List<string> AvoidableFeatureIds
{
get { return avoidableFeatureIds; }
}
public RoutingResult GetRoute()
{
if (stopPoints != null && !stopPoints.Any())
{
RoutingResult result = routingEngine.GetRoute(startPoint, endPoint);
return result;
}
else
{
Collection<PointShape> points = new Collection<PointShape>();
points.Add(startPoint);
foreach (PointShape stopPoint in stopPoints)
{
points.Add(stopPoint);
}
points.Add(endPoint);
// Filter the invalid point.
Collection<PointShape> availablePoint = FilterInvalidPoints(points);
RoutingResult result = null;
for (int i = 0; i < availablePoint.Count - 1; i++)
{
RoutingResult tempResult = routingEngine.GetRoute(availablePoint[i], availablePoint[i|+ 1]);
result = CombineRoutingResult(result, tempResult);
}
return result;
}
}
private Collection<PointShape> FilterInvalidPoints(IEnumerable<PointShape> points)
{
Collection<PointShape> result = new Collection<PointShape>();
barrierFeatureSource.Open();
Collection<Feature> barrierFeatures = barrierFeatureSource.GetAllFeatures(ReturningColumnsType.NoColumns);
foreach (PointShape point in points)
{
bool inBarrier = barrierFeatures.Any(a => a.Contains(new Feature(point)));
if (!inBarrier) result.Add(point);
}
return result;
}
private static RoutingResult CombineRoutingResult(RoutingResult baseResult, RoutingResult additionalResult)
{
if (baseResult == null)
{
baseResult = new RoutingResult();
baseResult.Distance = additionalResult.Distance;
baseResult.Weight = additionalResult.Weight;
baseResult.Route = additionalResult.Route;
additionalResult.Features.ForEach(a => baseResult.Features.Add(a));
additionalResult.OrderedStops.ForEach(a => baseResult.OrderedStops.Add(a));
additionalResult.RouteSegments.ForEach(a => baseResult.RouteSegments.Add(a));
}
else
{
baseResult.Distance += additionalResult.Distance;
baseResult.Weight += additionalResult.Weight;
Collection<Vertex> vertexes = new Collection<Vertex>();
baseResult.Route.Vertices.ForEach(vertexes.Add);
additionalResult.Route.Vertices.ForEach(vertexes.Add);
baseResult.Route = new LineShape(vertexes);
additionalResult.Features.ForEach(a => baseResult.Features.Add(a));
additionalResult.OrderedStops.ForEach(a => baseResult.OrderedStops.Add(a));
additionalResult.RouteSegments.ForEach(a => baseResult.RouteSegments.Add(a));
}
return baseResult;
}
private void RoutingAlgorithm_FindingRoute(object sender, FindingRouteRoutingAlgorithmEventArgs e)
{
Collection<string> beContainedFeatureIds = new Collection<string>();
barrierFeatureSource.Open();
Collection<string> startPointAdjacentIds = e.RouteSegment.StartPointAdjacentIds;
Collection<string> endPointAdjacentIds = e.RouteSegment.EndPointAdjacentIds;
foreach (string id in startPointAdjacentIds.Where(id => avoidableFeatureIds.Contains(id)))
{
beContainedFeatureIds.Add(id);
}
foreach (string id in endPointAdjacentIds.Where(id => avoidableFeatureIds.Contains(id)))
{
beContainedFeatureIds.Add(id);
}
// Remove the ones that be contained in the avoidable area
foreach (string id in beContainedFeatureIds)
{
if (e.RouteSegment.StartPointAdjacentIds.Contains(id))
{
e.RouteSegment.StartPointAdjacentIds.Remove(id);
}
if (e.RouteSegment.EndPointAdjacentIds.Contains(id))
{
e.RouteSegment.EndPointAdjacentIds.Remove(id);
}
}
}
}
}
StopItemViewModel.cs
using System;
using System.Windows.Media.Imaging;
using ThinkGeo.MapSuite.Core;
namespace ThinkGeo.MapSuite.RoutingExamples
{
public class StopItemViewModel : ViewModelBase
{
private const string markerImageSourceUri = "pack://application:,,,/CityRoutingAndDirections;Component/Image/stop.png";
private BitmapImage markerImageSource;
private PointShape location;
public StopItemViewModel()
: this(null)
{ }
public StopItemViewModel(PointShape location)
{
this.location = location;
this.MarkerImageSource = new BitmapImage(new Uri(markerImageSourceUri, UriKind.RelativeOrAbsolute));
}
public BitmapImage MarkerImageSource
{
get { return markerImageSource; }
set
{
markerImageSource = value;
RaisePropertyChanged(() => MarkerImageSource);
}
}
public PointShape Location
{
get { return location; }
set
{
location = value;
RaisePropertyChanged(() => Location);
}
}
}
}