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