/// <summary> /// Attempts to update either the address or the coordinates of the specified location /// if the other value is missing, using the specified current location to provide /// context for prioritizing multiple locations returned for an address. /// </summary> /// <param name="location">The location to update.</param> /// <param name="currentLocation">The current location.</param> public static async Task<bool> TryUpdateMissingLocationInfoAsync(LocationData location, LocationData currentLocation) { bool hasNoAddress = String.IsNullOrEmpty(location.Address); if (hasNoAddress && location.Position.Latitude == 0 && location.Position.Longitude == 0) return true; var results = hasNoAddress ? await MapLocationFinder.FindLocationsAtAsync(location.Geopoint) : await MapLocationFinder.FindLocationsAsync(location.Address, currentLocation.Geopoint); if (results.Status == MapLocationFinderStatus.Success && results.Locations.Count > 0) { var result = results.Locations.First(); // location.Position = result.Point.Position; location.Address = result.Address.FormattedAddress; if (String.IsNullOrEmpty(location.Name)) location.Name = result.Address.Town; // Sometimes the returned address is poorly formatted. This fixes one of the issues. if (location.Address.Trim().StartsWith(",")) location.Address = location.Address.Trim().Substring(1).Trim(); return true; } else { return false; } }
/// <summary> /// Attempts to update the travel distance and time info for the specified locations, /// relative to the current location, and raises an alert for each flagged location /// if traffic is currently increasing the travel time by 10 minutes or more. /// </summary> /// <param name="locations">The locations to update.</param> /// <param name="currentLocation">The current location, providing context to disambiguate locations, if needed. </param> /// <returns>true if all the locations were successfully updated; false if a service failure occurred.</returns> public static async Task<bool> TryUpdateLocationsTravelInfoAsync(IEnumerable<LocationData> locations, LocationData currentLocation) { try { await Task.WhenAll(locations.Select(async location => { await LocationHelper.UpdateTravelInfoAsync(location, currentLocation); int travelTimeDifference = location.CurrentTravelTime - location.CurrentTravelTimeWithoutTraffic; if (location.IsMonitored && travelTimeDifference >= 10) { LocationHelper.ShowToast( $"+{travelTimeDifference} min. to {location.Name}, total {location.CurrentTravelTime} min."); } })); return true; } catch (Exception ex) when (ex.Message.Equals(routeFinderUnavailableMessage)) { return false; } }
/// <summary> /// Updates the travel distance and time info for the specified location, relative to the specified current location. /// </summary> /// <param name="location">The location to update.</param> /// <param name="currentLocation">The current location.</param> public static async Task UpdateTravelInfoAsync(LocationData location, LocationData currentLocation) { var routeResultTask = MapRouteFinder.GetDrivingRouteAsync( currentLocation.Geopoint, location.Geopoint, MapRouteOptimization.TimeWithTraffic, MapRouteRestrictions.None); var routeResultWithoutTrafficTask = MapRouteFinder.GetDrivingRouteAsync( currentLocation.Geopoint, location.Geopoint, MapRouteOptimization.Time, MapRouteRestrictions.None); MapRouteFinderResult routeResult = await routeResultTask; MapRouteFinderResult routeResultWithoutTraffic = await routeResultWithoutTrafficTask; if (routeResult.Status == MapRouteFinderStatus.Success) { location.FastestRoute = routeResult.Route; location.CurrentTravelDistance = Math.Round(routeResult.Route.LengthInMeters * 0.00062137, 1); // convert to miles location.CurrentTravelTime = (int)routeResult.Route.EstimatedDuration.TotalMinutes; location.Timestamp = DateTimeOffset.Now; if (routeResultWithoutTraffic.Status == MapRouteFinderStatus.Success) { location.CurrentTravelTimeWithoutTraffic = routeResultWithoutTraffic.Route.EstimatedDuration.Minutes; } else { // Fall back to the with-traffic value if the request fails. location.CurrentTravelTimeWithoutTraffic = routeResult.Route.EstimatedDuration.Minutes; } } else throw new Exception(routeFinderUnavailableMessage); }
/// <summary> /// Copies the property values of the current location into the specified location. /// </summary> /// <param name="location">The location to receive the copied values.</param> public void Copy(LocationData location) { this.Name = location.Name; this.Address = location.Address; this.Position = location.Position; this.CurrentTravelDistance = location.CurrentTravelDistance; this.CurrentTravelTime = location.CurrentTravelTime; this.Timestamp = location.Timestamp; this.IsMonitored = location.IsMonitored; }
/// <summary> /// Launches the Maps app and displays the route from the current location /// to the specified location. /// </summary> /// <param name="location">The location to display the route to.</param> public static async Task ShowRouteToLocationInMapsAppAsync(LocationData location, LocationData currentLocation) { var mapUri = new Uri("bingmaps:?trfc=1&rtp=" + $"pos.{Math.Round(currentLocation.Position.Latitude, 6)}_{Math.Round(currentLocation.Position.Longitude, 6)}~" + $"pos.{location.Position.Latitude}_{location.Position.Longitude}"); await Windows.System.Launcher.LaunchUriAsync(mapUri); }
/// <summary> /// Gets the UI element that represents the specified location; /// used to access the attached editor flyout. /// <param name="location">The location to edit.</param> /// <returns>The element that represents the location.</returns> private FrameworkElement GetTemplateRootForLocation(LocationData location) { var item = this.LocationsView.ContainerFromItem(location) as ListViewItem; return item.ContentTemplateRoot as FrameworkElement; }
/// <summary> /// Return a new LocationData with the same property values as the current one. /// </summary> /// <returns>The new LocationData instance.</returns> public LocationData Clone() { var location = new LocationData(); location.Copy(this); return location; }
/// <summary> /// Applies the changes represented by the specified LocationData to /// the cached location in edit, saves the changes to the file system, /// and updates the user interface to account for the changes. /// </summary> private async Task SaveAsync(LocationData workingCopy) { Flyout.GetAttachedFlyout(this.GetTemplateRootForLocation(this.locationInEdit)).Hide(); this.isNewLocationInEdit = false; this.isExistingLocationBeingRepositioned = false; bool isAddressNew = workingCopy.Address != this.locationInEdit.Address; bool areCoordinatesNew = !workingCopy.Position.Equals(this.locationInEdit.Position); // If just the address OR just the coordinates are new, // clear the other value so that it can be updated. if (isAddressNew ^ areCoordinatesNew) { if (isAddressNew) workingCopy.Position = new BasicGeoposition(); if (areCoordinatesNew) workingCopy.Address = string.Empty; } // If the address, the coordinates, or both have changed, clear the travel // info and the route so that it doesn't reflect the old position. if (isAddressNew || areCoordinatesNew) { workingCopy.ClearTravelInfo(); this.InputMap.Routes.Clear(); } this.locationInEdit.Copy(workingCopy); var currentLocation = await this.GetCurrentLocationAsync(); if (currentLocation != null) { if (isAddressNew ^ areCoordinatesNew) { await LocationHelper.TryUpdateMissingLocationInfoAsync(this.locationInEdit, currentLocation); } } await LocationDataStore.SaveLocationDataAsync(this.Locations); if (currentLocation != null) { bool isNetworkAvailable = await this.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation); if (isNetworkAvailable) this.InputMap.Routes.Add(new MapRouteView(this.locationInEdit.FastestRoute)); } }
/// <summary> /// Opens the editor, binding it to a temporary copy of the current location /// that can be saved, or discarded if the user dismisses the editor. /// </summary> /// <param name="location"></param> private void EditLocation(LocationData location) { this.locationInEdit = location; var element = this.GetTemplateRootForLocation(location); var flyout = Flyout.GetAttachedFlyout(element) as Flyout; (flyout.Content as FrameworkElement).DataContext = location.Clone(); flyout.ShowAt(element); }
/// <summary> /// Adds the specified location to the Locations list and shows the editor flyout. /// </summary> public void EditNewLocation(LocationData location) { if (this.LocationsView.Visibility == Visibility.Collapsed) this.ToggleLocationsPaneVisibility(); this.Locations.Add(location); this.LocationsView.UpdateLayout(); this.isNewLocationInEdit = true; this.EditLocation(location); }
private async void TakePhotoButton_Click(object sender, RoutedEventArgs e) { BitmapImage bitmapSource = await PhotoHelper.GetPhotoFromCameraLaunch(); if (bitmapSource == null) return; // Specify a random location // var currentLocation = await LocationHelper.GetRandomGeoposition(); //Geopoint geopoint = new Geopoint(currentLocation); var currentLocation = await LocationHelper.GetCurrentLocationAsync(); LocationData location = new LocationData { Position = currentLocation.Geopoint.Position, ImageSource = bitmapSource }; this.Locations.Add(location); this.LocationsView.UpdateLayout(); LocationsView.SelectedItem = location; await LocationHelper.TryUpdateMissingLocationInfoAsync(location, null); }
/// <summary> /// Attempts to update the travel distance and time info for the specified locations, /// relative to the current location, and raises an alert for each flagged location /// if traffic is currently increasing the travel time by 10 minutes or more; also /// updates the network status message depending on the results. /// </summary> private async Task<bool> TryUpdateLocationsTravelInfoAsync(IEnumerable<LocationData> locations, LocationData currentLocation) { bool isNetworkAvailable = await LocationHelper.TryUpdateLocationsTravelInfoAsync(this.Locations, currentLocation); this.UpdateNetworkStatus(isNetworkAvailable); return isNetworkAvailable; }