4 using System.Windows.Navigation;
5 using Microsoft.Phone.Controls;
6 using Windows.Devices.Geolocation;
7 using System.Device.Location;
8 using System.Windows.Shapes;
9 using System.Windows.Media;
10 using Microsoft.Phone.Maps.Controls;
11 using System.Threading.Tasks;
12 using WeatherInformation.Model;
13 using System.IO.IsolatedStorage;
15 namespace WeatherInformation
17 public partial class MapPage : PhoneApplicationPage, ReverseGeoCode.IReverseGeoCode
19 private bool _isNewPageInstance;
20 private ReverseGeoCode _reverseOnProgress;
21 private Geolocator _geolocator;
22 // TODO: how big is a MapLayer object?
23 private MapOverlay _locationOverlay;
24 private Location _restoreLocation;
25 private GeoCoordinate _restoreReverseOnProgress;
29 InitializeComponent();
31 _isNewPageInstance = true;
34 protected override void OnNavigatedTo(NavigationEventArgs e)
36 AskForLocationConsent();
38 if (_isNewPageInstance)
42 if (State.ContainsKey("ReverseGeoCoorDinateOnProgress"))
44 this._restoreReverseOnProgress = (GeoCoordinate)State["ReverseGeoCoorDinateOnProgress"];
46 this._restoreLocation = (Location)State["CurrentChosenMapLocation"];
49 UpdateLocationStatus();
53 if (this._geolocator != null)
55 this._geolocator.StatusChanged += GeolocationStatusCallback;
58 // Set _isNewPageInstance to false. If the user navigates back to this page
59 // and it has remained in memory, this value will continue to be false.
60 _isNewPageInstance = false;
65 protected override void OnNavigatedFrom(NavigationEventArgs e)
67 if (e.NavigationMode != System.Windows.Navigation.NavigationMode.Back)
69 // TODO: Could Center be null?
70 State["CurrentChosenMapLocation"] = CoordinateHelper.GeoCoordinateToLocation(
71 mapWeatherInformation.Center,
72 LocationTextCity.Text,
73 LocationTextCountry.Text);
75 if (_reverseOnProgress != null)
77 State["ReverseGeoCoorDinateOnProgress"] = _reverseOnProgress.CoorDinate;
81 if (_geolocator != null)
83 _geolocator.StatusChanged -= GeolocationStatusCallback;
86 if (_reverseOnProgress != null)
88 _reverseOnProgress.Page = null;
92 public void OnCompletedReverseGeoCode(GeoCoordinate geoCoordinate, string city, string country)
94 UpdateMap(geoCoordinate, city, country);
96 _reverseOnProgress = null;
99 private async Task GetCurrentLocationAndUpdateMap(Geolocator geolocator)
103 var geoposition = await geolocator.GetGeopositionAsync(
104 maximumAge: TimeSpan.FromSeconds(1),
105 timeout: TimeSpan.FromSeconds(10)
108 var currentGeoCoordinate = CoordinateHelper.ConvertGeocoordinate(geoposition.Coordinate);
110 ReverseGeocodeAndUpdateMap(currentGeoCoordinate);
114 // TODO: hopefully using main thread when Exception.
115 if ((uint)ex.HResult == 0x80004004)
117 // Location is disabled in phone settings.
120 // TODO: not sure if when exception I will be using the calling thread calling the await method.
121 Dispatcher.BeginInvoke(new UpdateLocationButtonDelegate(this.UpdateLocationButton), false);
125 private void ReverseGeocodeAndUpdateMap(GeoCoordinate geoCoordinate)
127 var reverseGeoCode = new ReverseGeoCode()
130 CoorDinate = geoCoordinate
133 if (_reverseOnProgress != null)
135 // GC may release old object.
136 _reverseOnProgress.Page = null;
139 _reverseOnProgress = reverseGeoCode;
140 _reverseOnProgress.DoReverseGeocode(geoCoordinate);
143 private void UpdateMap(GeoCoordinate geoCoordinate, string city, string country)
145 // Create a small circle to mark the current location.
146 Ellipse myCircle = new Ellipse();
147 myCircle.Fill = new SolidColorBrush(Colors.Blue);
148 myCircle.Height = 20;
150 myCircle.Opacity = 50;
152 // Create a MapOverlay to contain the circle.
153 _locationOverlay = new MapOverlay();
154 _locationOverlay.Content = myCircle;
155 _locationOverlay.PositionOrigin = new Point(0.5, 0.5);
156 _locationOverlay.GeoCoordinate = geoCoordinate;
158 // Create a MapLayer to contain the MapOverlay.
159 MapLayer myLocationLayer = new MapLayer();
160 myLocationLayer.Add(_locationOverlay);
162 mapWeatherInformation.Layers.Clear();
164 // TODO: problems? user could press save location and if she is fast enough she
165 // could not realize the location changed.
166 // But well... She will realize later... So.. Is this really a problem?
167 mapWeatherInformation.Center = geoCoordinate;
168 mapWeatherInformation.ZoomLevel = 13;
170 LocationTextCity.Text = city;
171 LocationTextCountry.Text = country;
172 // Add the MapLayer to the Map.
173 mapWeatherInformation.Layers.Add(myLocationLayer);
176 private static class CoordinateHelper
178 public static GeoCoordinate ConvertGeocoordinate(Geocoordinate geocoordinate)
180 return new GeoCoordinate
182 geocoordinate.Latitude,
183 geocoordinate.Longitude,
184 geocoordinate.Altitude ?? Double.NaN,
185 geocoordinate.Accuracy,
186 geocoordinate.AltitudeAccuracy ?? Double.NaN,
187 geocoordinate.Speed ?? Double.NaN,
188 geocoordinate.Heading ?? Double.NaN
192 public static GeoCoordinate LocationToGeoCoordinate(Location locationItem)
194 return new GeoCoordinate
196 locationItem.Latitude,
197 locationItem.Longitude,
198 locationItem.Altitude,
199 locationItem.HorizontalAccuracy,
200 locationItem.VerticalAccuracy,
206 public static Location GeoCoordinateToLocation(GeoCoordinate geoCoordinate, string city, string country)
208 return new Location()
210 Latitude = geoCoordinate.Latitude,
211 Longitude = geoCoordinate.Longitude,
212 Altitude = geoCoordinate.Altitude,
215 HorizontalAccuracy = geoCoordinate.HorizontalAccuracy,
216 VerticalAccuracy = geoCoordinate.VerticalAccuracy,
217 Speed = geoCoordinate.Speed,
218 Course = geoCoordinate.Course,
220 LastRemoteDataUpdate = null,
225 // TODO: check data before storing :(
226 // http://stackoverflow.com/questions/4521435/what-specific-values-can-a-c-sharp-double-represent-that-a-sql-server-float-can
227 private void StoreLocation(GeoCoordinate geocoordinate, string city, string country)
229 using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
231 // Define the query to gather all of the to-do items.
232 // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
233 var locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault();
235 if (locationItem != null)
237 locationItem.Latitude = geocoordinate.Latitude;
238 locationItem.Longitude = geocoordinate.Longitude;
239 locationItem.Altitude = geocoordinate.Altitude;
240 locationItem.City = city;
241 locationItem.Country = country;
242 locationItem.HorizontalAccuracy = geocoordinate.HorizontalAccuracy;
243 locationItem.VerticalAccuracy = geocoordinate.VerticalAccuracy;
244 locationItem.Speed = geocoordinate.Speed;
245 locationItem.Course = geocoordinate.Course;
246 locationItem.IsSelected = true;
247 locationItem.LastRemoteDataUpdate = null;
251 locationItem = new Location()
253 Latitude = geocoordinate.Latitude,
254 Longitude = geocoordinate.Longitude,
255 Altitude = geocoordinate.Altitude,
258 HorizontalAccuracy = geocoordinate.HorizontalAccuracy,
259 VerticalAccuracy = geocoordinate.VerticalAccuracy,
260 Speed = geocoordinate.Speed,
261 Course = geocoordinate.Course,
263 LastRemoteDataUpdate = null,
266 // Add a location item to the local database.
267 db.Locations.InsertOnSubmit(locationItem);
274 private void mapWeatherInformation_Tap(object sender, System.Windows.Input.GestureEventArgs e)
276 // TODO: if exception from here application will crash (I guess)
277 var point = e.GetPosition(this.mapWeatherInformation);
278 GeoCoordinate geocoordinate = this.mapWeatherInformation.ConvertViewportPointToGeoCoordinate(point);
280 ReverseGeocodeAndUpdateMap(geocoordinate);
283 private async void GetCurrentLocationButton_Click(object sender, RoutedEventArgs e)
285 if (_geolocator == null)
291 // TODO: if exception from here application will crash (I guess)
292 await this.GetCurrentLocationAndUpdateMap(_geolocator);
295 private void SaveLocationButton_Click(object sender, RoutedEventArgs e)
297 // TODO: Could Center be null?
298 StoreLocation(this.mapWeatherInformation.Center, this.LocationTextCity.Text, this.LocationTextCountry.Text);
301 private async void UpdateLocationStatus()
303 if ((bool)IsolatedStorageSettings.ApplicationSettings["LocationConsent"] != true)
305 // The user has opted out of Location.
306 GetCurrentLocationButton.IsEnabled = false;
310 _geolocator = new Geolocator();
311 _geolocator.DesiredAccuracyInMeters = 50;
312 _geolocator.ReportInterval = 1000;
316 _geolocator.StatusChanged += GeolocationStatusCallback;
317 var geoposition = await _geolocator.GetGeopositionAsync(
318 maximumAge: TimeSpan.FromSeconds(1),
319 timeout: TimeSpan.FromSeconds(10)
324 if ((uint)ex.HResult == 0x80004004)
326 // Location is disabled in phone settings.
329 // TODO: not sure if when exception I will be using the calling thread calling the await method.
330 Dispatcher.BeginInvoke(new UpdateLocationButtonDelegate(this.UpdateLocationButton), false);
334 // TODO: how to do it just with lambda expressions?
335 delegate void UpdateLocationButtonDelegate(bool isEnabled);
336 private void UpdateLocationButton(bool isEnabled)
338 GetCurrentLocationButton.IsEnabled = isEnabled;
341 private void GeolocationStatusCallback(Geolocator sender, StatusChangedEventArgs eventData)
343 if (eventData.Status == PositionStatus.Ready)
345 Dispatcher.BeginInvoke(new UpdateLocationButtonDelegate(this.UpdateLocationButton), true);
349 Dispatcher.BeginInvoke(new UpdateLocationButtonDelegate(this.UpdateLocationButton), false);
353 private void AskForLocationConsent()
355 if (!IsolatedStorageSettings.ApplicationSettings.Contains("LocationConsent"))
357 MessageBoxResult result =
358 MessageBox.Show("This app accesses your phone's location. Is that ok?",
359 "Location", MessageBoxButton.OKCancel);
361 if (result == MessageBoxResult.OK)
363 IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = true;
367 IsolatedStorageSettings.ApplicationSettings["LocationConsent"] = false;
370 IsolatedStorageSettings.ApplicationSettings.Save();
374 private void RestoreUI()
377 if (this._restoreLocation != null)
379 location = this._restoreLocation;
381 else if (this._locationOverlay != null)
383 location = CoordinateHelper.GeoCoordinateToLocation(
384 _locationOverlay.GeoCoordinate,
385 LocationTextCity.Text,
386 LocationTextCountry.Text);
390 using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
392 // Define the query to gather all of the to-do items.
393 // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
394 location = db.Locations.Where(locationItem => locationItem.IsSelected).FirstOrDefault();
399 if (location != null)
401 UpdateMap(CoordinateHelper.LocationToGeoCoordinate(location),
402 location.City, location.Country);
405 if (this._restoreReverseOnProgress != null)
408 ReverseGeocodeAndUpdateMap(this._restoreReverseOnProgress);
416 private void ShowProgressBar()
418 GetCurrentLocationButton.Visibility = Visibility.Collapsed;
419 SaveLocationButton.IsEnabled = false;
420 SaveLocationButton.Visibility = Visibility.Collapsed;
421 ProgressBarRemoteData.IsEnabled = true;
422 ProgressBarRemoteData.Visibility = Visibility.Visible;
425 private void RemoveProgressBar()
427 GetCurrentLocationButton.Visibility = Visibility.Visible;
428 SaveLocationButton.IsEnabled = true;
429 SaveLocationButton.Visibility = Visibility.Visible;
430 ProgressBarRemoteData.IsEnabled = false;
431 ProgressBarRemoteData.Visibility = Visibility.Collapsed;
436 private void ZoomOutButton_Click(object sender, RoutedEventArgs e)
438 this.mapWeatherInformation.ZoomLevel = this.mapWeatherInformation.ZoomLevel - 1;
441 private void ZoomInButton_Click(object sender, RoutedEventArgs e)
443 this.mapWeatherInformation.ZoomLevel = this.mapWeatherInformation.ZoomLevel + 1;