Jak pokazać pozycję użytkownika na mapie

Wcześniej pisałem o kontrolce GMap do wyświetlania map Google-a w aplikacji WPF, która pozwala na wyświetlanie map w aplikacji WPF. Użycie kontrolki nie jest jakoś trudne. Wystarczy podglądnąć w przykładowym projekcie co i jak poustawiać aby wszystko zaczęło pracować. Co jednak jeśli chcielibyśmy wyświetlić pozycję użytkownika?… ale tak dynamicznie.

W Windows 7 znajduje się mechanizm pozwalający na dostęp do lokalizacji oraz sensorów. W panelu sterowania w sekcji sprzęt i dzwięk znajdziesz to co nas interesuje.

Tutaj możemy uzyskać dostęp do urządzeń lokalizacyjnych takich jak gps-y. Co jeżeli jednak nie mamy GPS-a w naszym komputerze? Jest rozwiązanie. Możemy pobrać lokalizację na podstawie ip użytkownika. Oczywiście takie określenie lokalizacji nie będzie zbytnio dokładne – w moim przypadku dokładność wynosi ok. 4-5km 🙂 – ale zawsze da nam to pewien pogląd na temat położenia maszyny. Jak to wykorzystamy, zależy tylko i wyłącznie od nas i naszej wyobraźni.

Jak zatem pobrać lokalizację na podstawie ip? Wystarczy zainstalować geosense for windows http://www.geosenseforwindows.com/ Sterownik ten symuluje urządzenie typu GPS tyle tylko że określa swoją pozycję na podstawie Google Location Services…. Tak, wykorzystamy wyniki danych zbieranych przez googla, o których tak głośno było niedawno.

Mając zainstalowany GeoSense for Windows będziemy potrzebowali coś do wyświetlenia pozycji. Do tego użyjemy http://greatmaps.codeplex.com/

Do projektu dodajemy referencje do GMap.NET.Core i GMap.NET.WindwosPresentation, które pozwolą nam korzystać z gmap. Cały xaml wygląda arcy skomplikowanie:

<UserControl x:Class=”Map.MapControl”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:my=”clr-namespace:System.Windows.Controls;assembly=GMap.NET.WindowsPresentation”
mc:Ignorable=”d”
d:DesignHeight=”229″ d:DesignWidth=”460″>
<Grid>
<my:GMapControl Name=”gMap”/>
</Grid>
</UserControl>

W pliku Cs w konstruktorze dodajemy:

Singleton<GMaps>.Instance.Mode = AccessMode.ServerOnly;gMap.MaxZoom = 0x11;gMap.MinZoom = 5;gMap.Zoom = 12;
To nam pozwoli na podstawowe wykorzystanie mapy.

Dodajemy obsługę sensorów. Dodajemy referencję do biblioteki Windows7.SensorAndLocation (do pobrania z tego miejsca http://code.msdn.microsoft.com/SensorsAndLocation/Release/ProjectReleases.aspx?ReleaseId=2359)

I dopisujemy dalszy kawałek kodu:

LatLongLocationProvider location = new LatLongLocationProvider(100);if (location.ReportStatus == ReportStatus.Running){

location.LocationChanged += new LocationChangedEventHandler(location_LocationChanged);
}
else if (location.ReportStatus == ReportStatus.AccessDenied)
{
//label1.Content = „Cannot retrieve information”;
}

To co robi powyższy kod, to praktycznie podpięcie się do zdarzeń obiektu LatLongLocationProvider. Zdarzenie to informuje o zmianie położenia. My to położenie wykorzystamy do zaktualizowania naszego miejsca na mapie jak poniżej:

var location = (LatLongLocationReport)locationProvider.GetReport();this.InvokeIfRequired((value) => gMap.CurrentPosition = new PointLatLng(location.Latitude, location.Longitude), String.Empty);

Pierwsza linijka daje nam szerokość I długość geograficzną; druga linikja wykorzystuje te dane do zaktualizowania pozycji na mapie. Na co warto tu zwrócić uwagę, to fakt, że LocationChanged jest wywoływane z innego wątku niż wątek interfejsu użytkownika, dlatego musimy trochę pobawić się aby zaktualizować pozycję.

Dla ciekawskich, metoda InvokeIfRequred wygląda tak:

 public static void InvokeIfRequired<T>(this Control control, Action<T> action, T parameter){if (System.Threading.Thread.CurrentThread != control.Dispatcher.Thread)                control.Dispatcher.Invoke(action, parameter);            else                action(parameter);}

Z uwagi na późną godzinę, powyższe rozwiązanie jest typu Quick n Dirty ale działa. Teraz pozostaje obudowanie całości w coś sensowniejszego oraz uwzględnienie faktu, że użytkownik niekoniecznie musi posiadać dostęp do sensorów – nie mówiąc o tym, że w systemie może w ogóle nie być sensorów. Szczegóły implementacji i działania do podglądnięcia w źródłach aplikacji DesktopInfo.