< Summary

Information
Class: RouteEditorCS.MainViewModel
Assembly: RouteEditorCS
File(s): D:\a\RouteEditorCS\RouteEditorCS\MainViewModel.cs
Tag: 9_19405000996
Line coverage
37%
Covered lines: 71
Uncovered lines: 120
Coverable lines: 191
Total lines: 437
Line coverage: 37.1%
Branch coverage
23%
Covered branches: 14
Total branches: 60
Branch coverage: 23.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_GuiZoomInCommand()100%210%
get_GuiZoomOutCommand()100%210%
get_BeforeClosingCommand()100%210%
get_RemoveRouteCommand()100%210%
get_LoadRouteCommand()100%210%
get_SaveRouteCommand()100%210%
get_Items()100%11100%
get_WindowTitle()100%11100%
set_WindowTitle(...)100%22100%
get_StatusLine()100%210%
set_StatusLine(...)100%22100%
get_GuiZoomFactor()100%210%
set_GuiZoomFactor(...)100%22100%
get_SelectedMap()100%210%
set_SelectedMap(...)100%11100%
OnPropertyChanged(...)100%22100%
.ctor(...)100%11100%
GuiZoomIn()100%210%
GuiZoomOut()100%210%
HandleMapChanges()75%4.07483.33%
BeforeClosing()100%210%
OnPositionChanged(...)50%2.04277.77%
RemoveRoute()0%620%
SaveRoute()0%110100%
LoadRoute()0%272160%
ShowRoute()0%4260%
DistanceKm(...)100%210%
DegreesToRadians(...)100%210%
RouteLengthKm()33.33%24.43620%
UpdateMousePositionFrom(...)100%210%
AddRoutePoint()0%620%
RemoveLastRoutePoint()0%2040%

File(s)

D:\a\RouteEditorCS\RouteEditorCS\MainViewModel.cs

#LineLine coverage
 1using GMap.NET;
 2using GMap.NET.MapProviders;
 3using GMap.NET.WindowsPresentation;
 4using RouteEditorCS.Properties;
 5using Ookii.Dialogs.Wpf;
 6using System.ComponentModel;
 7using System.Globalization;
 8using System.Net.NetworkInformation;
 9using System.Text;
 10using System.Windows.Input;
 11using System.Windows.Media;
 12
 13namespace RouteEditorCS
 14{
 15    /// <summary>
 16    /// MainViewModel implements the UI functions joins them with data and files.
 17    /// </summary>
 18    public class MainViewModel : INotifyPropertyChanged
 19    {
 20
 21        private string _windowTitle;
 22        private double _guiZoomFactor;
 23        private string _statusLine;
 24        private string _selectedMap;
 25
 26        private PointLatLng _mouseDownPos;
 27        private List<PointLatLng> _routePoints;
 28        private GMapRoute _route;
 29
 30        private readonly IGMapControl _gmapControl;
 31
 32        public event PropertyChangedEventHandler PropertyChanged;
 033        public ICommand GuiZoomInCommand => new RelayCommand(GuiZoomIn);
 034        public ICommand GuiZoomOutCommand => new RelayCommand(GuiZoomOut);
 035        public ICommand BeforeClosingCommand => new RelayCommand(BeforeClosing);
 036        public ICommand RemoveRouteCommand => new RelayCommand(RemoveRoute);
 037        public ICommand LoadRouteCommand => new RelayCommand(LoadRoute);
 038        public ICommand SaveRouteCommand => new RelayCommand(SaveRoute);
 39
 440        public ProviderCollection Items { get; }
 41
 42        public string WindowTitle
 43        {
 444            get => _windowTitle;
 45            set
 46            {
 1047                if (value != null)
 48                {
 849                    _windowTitle = value;
 850                    OnPropertyChanged(nameof(WindowTitle));
 51                }
 1052            }
 53        }
 54
 55        public string StatusLine
 56        {
 057            get => _statusLine;
 58            set
 59            {
 460                if (value != null)
 61                {
 462                    _statusLine = value;
 463                    OnPropertyChanged(nameof(StatusLine));
 64                }
 465            }
 66        }
 67
 68        public double GuiZoomFactor
 69        {
 070            get { return _guiZoomFactor; }
 71            set
 72            {
 473                if (_guiZoomFactor != value)
 74                {
 475                    _guiZoomFactor = value;
 476                    OnPropertyChanged(nameof(GuiZoomFactor));
 77                }
 478            }
 79        }
 80        public string SelectedMap
 81        {
 082            get => _selectedMap;
 83            set
 84            {
 485                _selectedMap = value;
 486                OnPropertyChanged(nameof(SelectedMap));
 487                HandleMapChanges();
 488            }
 89        }
 90        protected void OnPropertyChanged (string propertyName)
 91        {
 2092            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 293        }
 94
 95        /// <summary>
 96        /// The Model Constructor needs the map control object.
 97        /// </summary>
 98        /// <param name="gmapControl">most functions of the UI are focused on the MapControl</param>
 499        public MainViewModel (IGMapControl gmapControl) {
 100
 101            // @todo make this init more configurable
 102
 103            // for testing
 104            //Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
 105
 4106            WindowTitle = GetType().Namespace;
 4107            _gmapControl = gmapControl;
 108
 4109            GuiZoomFactor = Settings.Default.guiSize;
 110
 4111            Items = new ProviderCollection(new[]
 4112            {
 4113                new ProviderItem(
 4114                    "OSM",
 4115                    "OpenStreetMap",
 4116                    OpenStreetMapProvider.Instance
 4117                ),
 4118                new ProviderItem(
 4119                    "googlemaps",
 4120                    "Google Maps",
 4121                    GMapProviders.GoogleMap
 4122                ),
 4123                new ProviderItem(
 4124                    "opencyclemap",
 4125                    "Cycle Maps",
 4126                    GMapProviders.OpenCycleMap
 4127                ),
 4128                new ProviderItem(
 4129                    "BingHybrid",
 4130                    "Bing Hybrid",
 4131                    GMapProviders.BingHybridMap
 4132                )
 4133            });
 4134            SelectedMap = Settings.Default.lastMap;
 4135            _gmapControl.Zoom = Settings.Default.lastZoom;
 4136            _gmapControl.ShowCenter = false;
 4137            _gmapControl.CanDragMap = true;
 4138            _gmapControl.DragButton = MouseButton.Left;
 139
 4140            _gmapControl.Position = new PointLatLng(
 4141                Settings.Default.lastLatitude,
 4142                Settings.Default.lastLongitude
 4143            );
 144
 4145            OnPositionChanged(_gmapControl.Position);
 4146        }
 147
 148        /// <summary>
 149        /// UI Button function to make the UI bigger.
 150        /// </summary>
 151        public void GuiZoomIn ()
 152        {
 0153            GuiZoomFactor *= 1.1;
 0154            Settings.Default.guiSize = _guiZoomFactor;
 0155            Settings.Default.Save();
 0156        }
 157
 158        /// <summary>
 159        /// UI Button function to make the UI smaller.
 160        /// </summary>
 161        public void GuiZoomOut ()
 162        {
 0163            GuiZoomFactor /= 1.1;
 0164            Settings.Default.guiSize = _guiZoomFactor;
 0165            Settings.Default.Save();
 0166        }
 167
 168        /// <summary>
 169        /// This method sets map provider and cache based on the _selectedMap and settings.
 170        /// </summary>
 171        private void HandleMapChanges ()
 172        {
 4173            if (NetworkInterface.GetIsNetworkAvailable())
 174            {
 4175                _gmapControl.CacheMode = AccessMode.ServerAndCache;
 176            }
 177            else
 178            {
 0179                _gmapControl.CacheMode = AccessMode.CacheOnly;
 180            }
 181
 4182            if (Items.TryGet(_selectedMap, out var item))
 183            {
 4184                _gmapControl.MapProvider = item.Provider;
 185            }
 4186        }
 187
 188        /// <summary>
 189        /// Store position, map zoom and map provider before closing the application.
 190        /// This method is called by the UI in the OnClose Event.
 191        /// </summary>
 192        private void BeforeClosing ()
 193        {
 0194            Settings.Default.lastLatitude = _gmapControl.Position.Lat;
 0195            Settings.Default.lastLongitude = _gmapControl.Position.Lng;
 0196            Settings.Default.lastZoom = (int)_gmapControl.Zoom;
 0197            Settings.Default.lastMap = _selectedMap;
 0198            Settings.Default.Save();
 0199        }
 200
 201        /// <summary>
 202        /// This method refreshs the statusline with coordinates and route length.
 203        /// It is called by mouse events, which occurs on the mapControl object.
 204        /// </summary>
 205        /// <param name="point">the latitude and longitude calculated by the UI event.</param>
 206        private void OnPositionChanged (PointLatLng point)
 207        {
 4208            string formattedLat = point.Lat.ToString("F6", CultureInfo.InvariantCulture);
 4209            string formattedLng = point.Lng.ToString("F6", CultureInfo.InvariantCulture);
 4210            string zoom = _gmapControl.Zoom.ToString("F1", CultureInfo.InvariantCulture);
 211
 4212            double distance = RouteLengthKm();
 4213            if (distance > 0)
 214            {
 0215                string formattedDist = distance.ToString("F4", CultureInfo.InvariantCulture);
 0216                StatusLine = $"Lat Lng [Zoom] Route: {formattedLat} {formattedLng} [{zoom}] {formattedDist} km";
 217            }
 218            else
 219            {
 4220                StatusLine = $"Lat Lng [Zoom]: {formattedLat} {formattedLng} [{zoom}]";
 221            }
 4222        }
 223
 224        /// <summary>
 225        /// UI Button function to delete the route from the map.
 226        /// </summary>
 227        private void RemoveRoute()
 228        {
 0229            if (_route != null)
 230            {
 0231                _gmapControl.Markers.Remove(_route);
 0232                _route = null;
 233            }
 0234            _routePoints = new List<PointLatLng>();
 0235            StatusLine = "";
 0236        }
 237
 238        /// <summary>
 239        /// UI Button function to open a save dialog.
 240        /// </summary>
 241        private void SaveRoute ()
 242        {
 0243            if (_routePoints == null || _routePoints.Count == 0) return;
 244
 0245            var dlg = new VistaSaveFileDialog
 0246            {
 0247                Title = Strings.SaveDialog_Title,
 0248                Filter = Strings.Dialog_Filetype + " (*.txt)|*.txt",
 0249                DefaultExt = "txt",
 0250                FileName = "route.txt",
 0251                OverwritePrompt = true
 0252            };
 253
 0254            bool? result = dlg.ShowDialog();
 0255            if (result != true) return;
 256
 0257            var culture = CultureInfo.InvariantCulture;
 258
 0259            var sb = new StringBuilder();
 0260            double distance = 0.0;
 0261            for (int i = 0; i < _routePoints.Count; i++)
 262            {
 0263                double lat = _routePoints[i].Lat;
 0264                double lng = _routePoints[i].Lng;
 0265                if (i > 0)
 266                {
 0267                    distance += DistanceKm(_routePoints[i - 1], _routePoints[i]);
 268                }
 269
 0270                sb.AppendLine(string.Format(culture, "{0:F6},{1:F6},{2:F4}km", lat, lng, distance));
 271            }
 272
 0273            System.IO.File.WriteAllText(dlg.FileName, sb.ToString(), Encoding.UTF8);
 0274        }
 275
 276        /// <summary>
 277        /// UI Button function to open a file-open dialog to load a route.
 278        /// </summary>
 279        private void LoadRoute ()
 280        {
 0281            var dialog = new VistaOpenFileDialog
 0282            {
 0283                Title = Strings.LoadDialog_Title,
 0284                Filter = Strings.Dialog_Filetype + " (*.txt)|*.txt",
 0285                DefaultExt = "txt",
 0286                Multiselect = false,
 0287                CheckFileExists = true,
 0288                CheckPathExists = true
 0289            };
 290
 0291            bool? result = dialog.ShowDialog();
 292
 0293            if (result == true)
 294            {
 0295                var culture = CultureInfo.InvariantCulture;
 296
 0297                if (_route != null)
 298                {
 0299                    _gmapControl.Markers.Remove(_route);
 300                }
 0301                _routePoints = new List<PointLatLng>();
 302
 0303                foreach (var line in System.IO.File.ReadLines(dialog.FileName))
 304                {
 0305                    if (string.IsNullOrWhiteSpace(line)) continue;
 0306                    var parts = line.Split(new[] { ' ', ',', '\t' }, StringSplitOptions.RemoveEmptyEntries);
 0307                    if (parts.Length < 2)
 308                    {
 309                        continue;
 310                    }
 0311                    if (
 0312                        double.TryParse(parts[0], NumberStyles.Float | NumberStyles.AllowLeadingSign, culture, out doubl
 0313                        double.TryParse(parts[1], NumberStyles.Float | NumberStyles.AllowLeadingSign, culture, out doubl
 0314                    )
 315                    {
 0316                        _routePoints.Add(new PointLatLng(v1, v2));
 317                    }
 318                    // @todo error handling like a log or StatusLine
 319                }
 0320                if (_routePoints.Count > 1)
 321                {
 0322                    _gmapControl.Position = _routePoints.Last();
 0323                    ShowRoute();
 324                }
 325            }
 0326        }
 327
 328        /// <summary>
 329        /// This method is called on load route or if a new point is added to the route or a point is removed.
 330        /// </summary>
 331        private void ShowRoute ()
 332        {
 0333            if (_routePoints == null)
 334            {
 0335                _routePoints = new List<PointLatLng>();
 336            }
 337
 0338            if (_route != null)
 339            {
 0340                _gmapControl.Markers.Remove(_route);
 341            }
 342
 0343            if (_routePoints.Count > 0)
 344            {
 0345                _route = new GMapRoute(_routePoints);
 0346                _route.Shape = new System.Windows.Shapes.Path()
 0347                {
 0348                    Stroke = new SolidColorBrush(Colors.Blue),
 0349                    StrokeThickness = 2
 0350                };
 351
 0352                _gmapControl.Markers.Add(_route);
 353            }
 0354        }
 355
 356        /// <summary>
 357        /// Primitive methode to calculate the distance between to positions on the earth.
 358        /// </summary>
 359        /// <param name="p1">position 1</param>
 360        /// <param name="p2">position 2</param>
 361        /// <returns></returns>
 362        private double DistanceKm (PointLatLng p1, PointLatLng p2)
 363        {
 364            const double R = 6371.0; // Erdradius in km
 0365            double lat1 = DegreesToRadians(p1.Lat);
 0366            double lat2 = DegreesToRadians(p2.Lat);
 0367            double dLat = DegreesToRadians(p2.Lat - p1.Lat);
 0368            double dLon = DegreesToRadians(p2.Lng - p1.Lng);
 369
 0370            double a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
 0371                       Math.Cos(lat1) * Math.Cos(lat2) *
 0372                       Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
 0373            double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
 0374            return R * c;
 375        }
 376
 377        /// <summary>
 378        /// converter used by DistanceKm() to get positions in radians and not degrees
 379        /// </summary>
 380        /// <param name="deg">angle by degrees</param>
 381        /// <returns>the angle in radians</returns>
 382        private double DegreesToRadians (double deg)
 383        {
 0384            return deg * (Math.PI / 180.0);
 385        }
 386
 387        /// <summary>
 388        /// method loops through _routePoints and calculate + sum the route distance.
 389        /// </summary>
 390        /// <returns>the complete distance of the route stored in _routePoints</returns>
 391        private double RouteLengthKm ()
 392        {
 8393            if (_routePoints == null || _routePoints.Count < 2) return 0.0;
 0394            double total = 0.0;
 0395            for (int i = 0; i < _routePoints.Count - 1; i++)
 396            {
 0397                total += DistanceKm(_routePoints[i], _routePoints[i + 1]);
 398            }
 0399            return total;
 400        }
 401
 402        /// <summary>
 403        /// Used by UI events to store the map positon on earth in _mouseDownPos
 404        /// </summary>
 405        /// <param name="point"></param>
 406        public void UpdateMousePositionFrom (System.Windows.Point point)
 407        {
 0408            _mouseDownPos = _gmapControl.FromLocalToLatLng((int)point.X, (int)point.Y);
 0409            OnPositionChanged(_mouseDownPos);
 0410        }
 411
 412        /// <summary>
 413        /// Used by UI event on mapControl object and add a point in _routePoints
 414        /// </summary>
 415        public void AddRoutePoint ()
 416        {
 417            // to modern for mono csc
 418            //_routePoints ??= new List<PointLatLng>();
 0419            if (_routePoints == null) _routePoints = new List<PointLatLng>();
 0420            _routePoints.Add(_mouseDownPos);
 0421            ShowRoute();
 0422        }
 423
 424        /// <summary>
 425        /// Used by UI event on mapControl object and remove a point in _routePoints
 426        /// </summary>
 427        public void RemoveLastRoutePoint ()
 428        {
 0429            if (_routePoints != null && _routePoints.Count > 0)
 430            {
 0431                _routePoints.RemoveAt(_routePoints.Count - 1);
 0432                ShowRoute();
 433            }
 0434        }
 435
 436    }
 437}