2f77793301268a03e715e46899e70696b60df82b
[CSharpForFun/.git] / WindowsPhone / WP8 / WeatherInformation / WeatherInformation / MapPage.xaml.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Windows;
5 using System.Windows.Navigation;
6 using Microsoft.Phone.Controls;
7 using Windows.Devices.Geolocation;
8 using System.Device.Location;
9 using System.Windows.Shapes;
10 using System.Windows.Media;
11 using Microsoft.Phone.Maps.Controls;
12 using WeatherInformation.Resources;
13 using System.Globalization;
14 using Microsoft.Phone.Maps.Services;
15 using System.Threading.Tasks;
16 using WeatherInformation.Model;
17
18 namespace WeatherInformation
19 {
20     public partial class MapPage : PhoneApplicationPage
21     {
22         // TODO anything better than these two instance fields? :(
23         private string _city;
24         private string _country;
25
26         public MapPage()
27         {
28             InitializeComponent();
29         }
30
31         protected override void OnNavigatedTo(NavigationEventArgs e)
32         {
33             // TODO: I am not saving the UI state. If location changed but it was not saved
34             // user will have to pick her location again :(
35             Location locationItem = null;
36             using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
37             {
38                 // Define the query to gather all of the to-do items.
39                 // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
40                 locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault();
41             }
42             
43             if (locationItem != null)
44             {
45                 GeoCoordinate geoCoordinate = ConvertLocation(locationItem);
46
47                 this.UpdateMap(geoCoordinate, locationItem.City, locationItem.Country);
48             }
49         }
50
51         private async Task GetCurrentLocationAndUpdateMap()
52         {
53             Geolocator geolocator = new Geolocator();
54             geolocator.DesiredAccuracyInMeters = 50;
55
56             Geoposition geoposition = await geolocator.GetGeopositionAsync(
57                 maximumAge: TimeSpan.FromSeconds(10),
58                 timeout: TimeSpan.FromSeconds(10)
59                 );
60             GeoCoordinate currentGeoCoordinate = CoordinateHelper.ConvertGeocoordinate(geoposition.Coordinate);
61
62             ReverseGeocodeAndUpdateMap(currentGeoCoordinate);
63         }
64
65         // TODO: problems updating Map because this method may be called when automatically retrieving
66         //       the current user's location or when user taps on map tyring to choose by herself her location.
67         //       There could be 2 threads updating Map at the same time. Solution: remove the feature
68         //       of getting the current user's location (user must always choose her/his location instead of doing
69         //       it automatically)
70         private void ReverseGeocodeAndUpdateMap(GeoCoordinate currentGeoCoordinate)
71         {
72             ReverseGeocodeQuery currentReverseGeocodeQuery = new ReverseGeocodeQuery();
73             currentReverseGeocodeQuery.GeoCoordinate = currentGeoCoordinate;
74             currentReverseGeocodeQuery.QueryCompleted += QueryCompletedCallback;
75             currentReverseGeocodeQuery.QueryAsync();
76         }
77
78         private void QueryCompletedCallback(object sender, QueryCompletedEventArgs<IList<MapLocation>> eventData)
79         {
80             if (eventData.Cancelled)
81             {
82                 // Be careful!!! If you throw exception from this point your program will finish with "Unhandled Exception".
83                 return;
84             }
85
86             Exception errorException = eventData.Error;
87             if (errorException != null)
88             {
89                 // TODO: if user changed the page, where is this going to appear?
90                 MessageBox.Show(
91                     AppResources.NoticeErrorLocationAutodetection,
92                     AppResources.UnavailableAutomaticCurrentLocationMessageBox,
93                     MessageBoxButton.OK);
94             }
95             else
96             {
97                 if (eventData.Result.Count > 0)
98                 {
99                     // TODO: Should I call UpdateMap even if there are not results?
100                     // I could use country and city default values in that case...
101                     // Problem this method: requires GeoCoordinate and I wouldn't have it here...
102                     // Somehow this method should take them...
103                     MapAddress address = eventData.Result[0].Information.Address;
104                     GeoCoordinate currentGeoCoordinate = eventData.Result[0].GeoCoordinate;
105
106                     UpdateMap(currentGeoCoordinate, address.City, address.Country);
107                 }
108             }
109         }
110
111         private void UpdateMap(GeoCoordinate geoCoordinate, string city, string country)
112         {
113             // Create a small circle to mark the current location.
114             Ellipse myCircle = new Ellipse();
115             myCircle.Fill = new SolidColorBrush(Colors.Blue);
116             myCircle.Height = 20;
117             myCircle.Width = 20;
118             myCircle.Opacity = 50;
119
120             // Create a MapOverlay to contain the circle.
121             MapOverlay myLocationOverlay = new MapOverlay();
122             myLocationOverlay.Content = myCircle;
123             myLocationOverlay.PositionOrigin = new Point(0.5, 0.5);
124             myLocationOverlay.GeoCoordinate = geoCoordinate;
125
126             // Create a MapLayer to contain the MapOverlay.
127             MapLayer myLocationLayer = new MapLayer();
128             myLocationLayer.Add(myLocationOverlay);
129
130             this.mapWeatherInformation.Layers.Clear();
131
132             // TODO: problems? user could press save location and if she is fast enough she
133             // could not realize the location changed.
134             // But well... She will realize later... So.. Is this really a problem?
135             this.mapWeatherInformation.Center = geoCoordinate;
136             this.mapWeatherInformation.ZoomLevel = 13;
137
138             if (string.IsNullOrEmpty(city))
139             {
140                 city = AppResources.DefaultCity;
141             }
142             if (string.IsNullOrEmpty(country))
143             {
144                 country = AppResources.DefaultCountry;
145             }
146             this.LocationTextCityCountry.Text = String.Format(CultureInfo.InvariantCulture, "{0}, {1}", city, country);
147             _city = city;
148             _country = country;
149             // Add the MapLayer to the Map.
150             this.mapWeatherInformation.Layers.Add(myLocationLayer);
151         }
152
153         private static class CoordinateHelper
154         {
155             public static GeoCoordinate ConvertGeocoordinate(Geocoordinate geocoordinate)
156             {
157                 return new GeoCoordinate
158                     (
159                     geocoordinate.Latitude,
160                     geocoordinate.Longitude,
161                     geocoordinate.Altitude ?? Double.NaN,
162                     geocoordinate.Accuracy,
163                     geocoordinate.AltitudeAccuracy ?? Double.NaN,
164                     geocoordinate.Speed ?? Double.NaN,
165                     geocoordinate.Heading ?? Double.NaN
166                     );
167             }
168         }
169
170         // TODO: check data before storing :(
171         // http://stackoverflow.com/questions/4521435/what-specific-values-can-a-c-sharp-double-represent-that-a-sql-server-float-can
172         private void StoreLocation(GeoCoordinate geocoordinate)
173         {
174             Location locationItem = null;
175             using (var db = new LocationDataContext(LocationDataContext.DBConnectionString))
176             {
177                 // Define the query to gather all of the to-do items.
178                 // var toDoItemsInDB = from Location location in _locationDB.Locations where location.IsSelected select location;
179                 locationItem = db.Locations.Where(location => location.IsSelected).FirstOrDefault();
180
181                 if (locationItem != null)
182                 {
183                     locationItem.Latitude = geocoordinate.Latitude;
184                     locationItem.Longitude = geocoordinate.Longitude;
185                     locationItem.Altitude = geocoordinate.Altitude;
186                     locationItem.City = _city ?? "";
187                     locationItem.Country = _country ?? "";
188                     locationItem.HorizontalAccuracy = geocoordinate.HorizontalAccuracy;
189                     locationItem.VerticalAccuracy = geocoordinate.VerticalAccuracy;
190                     locationItem.Speed = geocoordinate.Speed;
191                     locationItem.Course = geocoordinate.Course;
192                     locationItem.IsSelected = true;
193                     locationItem.LastRemoteDataUpdate = null;
194                 }
195                 else
196                 {
197                     locationItem = new Location()
198                     {
199                         Latitude = geocoordinate.Latitude,
200                         Longitude = geocoordinate.Longitude,
201                         Altitude = geocoordinate.Altitude,
202                         City = _city ?? "",
203                         Country = _country ?? "",
204                         HorizontalAccuracy = geocoordinate.HorizontalAccuracy,
205                         VerticalAccuracy = geocoordinate.VerticalAccuracy,
206                         Speed = geocoordinate.Speed,
207                         Course = geocoordinate.Course,
208                         IsSelected = true,
209                         LastRemoteDataUpdate = null,
210                     };
211
212                     // Add a location item to the local database.
213                     db.Locations.InsertOnSubmit(locationItem);
214                 }
215
216                 db.SubmitChanges();
217             }
218         }
219
220         private GeoCoordinate ConvertLocation(Location locationItem)
221         {
222             return new GeoCoordinate
223                 (
224                 locationItem.Latitude,
225                 locationItem.Longitude,
226                 locationItem.Altitude,
227                 locationItem.HorizontalAccuracy,
228                 locationItem.VerticalAccuracy,
229                 locationItem.Speed,
230                 locationItem.Course
231                 );
232         }
233
234         private void mapWeatherInformation_Tap(object sender, System.Windows.Input.GestureEventArgs e)
235         {
236             var point = e.GetPosition(this.mapWeatherInformation);
237             GeoCoordinate geocoordinate = this.mapWeatherInformation.ConvertViewportPointToGeoCoordinate(point);
238             ReverseGeocodeAndUpdateMap(geocoordinate);
239         }
240
241         private async void GetCurrentLocationButton_Click(object sender, RoutedEventArgs e)
242         {
243             try
244             {
245                 await this.GetCurrentLocationAndUpdateMap();
246             }
247             catch (Exception ex)
248             {
249                 // TODO: make sure when exception in GetCurrentLocationAndUpdateMap we catch it here.
250                 // TODO: if user changed the page, where is this going to appear?
251                 MessageBox.Show(
252                     AppResources.NoticeErrorLocationAutodetection,
253                     AppResources.UnavailableAutomaticCurrentLocationMessageBox,
254                     MessageBoxButton.OK);
255             }
256         }
257
258         private void SaveLocationButton_Click(object sender, RoutedEventArgs e)
259         {
260             // TODO: Could there some problem if user clicks button and thread is in this very moment updating map?
261             var geoCoordinate = this.mapWeatherInformation.Center;
262
263             StoreLocation(geoCoordinate);
264         }
265
266         private void ZoomOutButton_Click(object sender, RoutedEventArgs e)
267         {
268             this.mapWeatherInformation.ZoomLevel = this.mapWeatherInformation.ZoomLevel - 1;
269         }
270
271         private void ZoomInButton_Click(object sender, RoutedEventArgs e)
272         {
273             this.mapWeatherInformation.ZoomLevel = this.mapWeatherInformation.ZoomLevel + 1;
274         }
275     }
276 }