Lokalizacja
Autor: Marcin Kruszyński
Opublikowano: 2011-03-04
Urządzenia z Windows Phone 7 mają możliwość ustalenia swojego położenia geograficznego za pomocą serwisu lokalizacji. Dane są uzyskiwane z kilku różnych źródeł.
Po przeczytaniu tego artykułu będziesz:
- wiedział, za pomocą jakich źródeł serwis lokalizacji ustala położenie,
- znał architekturę serwisu lokalizacji i jego właściwości,
- potrafił skorzystać z serwisu lokalizacji w swoich aplikacjach.
Wprowadzenie
Przy korzystaniu z serwisu lokalizacji powinniśmy być świadomi następujących uwarunkowań:
- dane o położeniu mogą nie być dostępne przez cały czas,
- ich dokładność zależy od rodzaju informacji dostarczanych przez sensory i od ustawień samego serwisu,
- używanie serwisu lokalizacji, zwłaszcza przy większej dokładności, powoduje większe zużycie baterii,
- inicjalizacja i uzyskanie pierwszego odczytu zajmuje trochę czasu,
- użytkownik może wyłączyć serwis lokalizacji na telefonie w ustawieniach systemowych.
Architektura i właściwości
**Rys.*1. Pozyskiwanie informacji o lokalizacji. *
Rysunek 1 pokazuje metody odczytywania położenia i związanych z nim danych stosowane przez serwis lokalizacji na telefonie z Windows Phone 7.
Architektura tego serwisu jest trójwarstwowa.
Pierwsza warstwa obejmuje sprzęt, w którego skład wchodzą:
- wbudowany odbiornik GPS,
- element odczytujący położenia stacji sieci komórkowej, z którymi komunikuje się telefon,
- element pozyskujący informacje z połączeń Wi-Fi.
Wymagana przez aplikację dokładność i zużycie baterii są czynnikami, które wpływają na wybór źródeł danych. Wyznaczanie położenia za pomocą sieci komórkowej i Wi-Fi cechuje mniejsza dokładność i mniejsze zużycie energii. Natomiast odbiornik GPS pobiera więcej energii, ale ma większą precyzję.
Nad warstwą sprzętu znajduje się warstwa natywnego kodu i to w niej zawarta jest cała logika. Natywny kod komunikuje się bezpośrednio ze wszystkimi źródłami danych i podejmuje decyzję, którego z nich użyć w zależności od ich dostępności i wymaganego poziomu dokładności. Komunikuje się także z webserwisem firmy Microsoft w celu pobrania z bazy danych informacji związanych z lokalizacją.
Najwyższą warstwą jest warstwa kodu zarządzanego. Dzięki niej nasze aplikacje mogą uruchamiać i zatrzymywać serwis lokalizacji, ustawiać wymagany poziom dokładności oraz odbierać informacje o położeniu. Dostarczone API w Windows Phone 7 jest na ogół zgodne z API w .NET Framework 4.
Dobrą praktyką jest włączanie serwisu lokalizacji tylko wtedy, gdy jest wymagany. Warto używać też mniejszej dokładności ze względu na mniejsze zużycie energii. Z wysokiej dokładności korzystajmy tylko w sytuacjach, gdy jest to naprawdę potrzebne. W wielu przypadkach wystarczy jedynie znajomość miasta lub regionu, w którym znajduje się urządzenie.
Implementacja
Aplikacje mogą korzystać z serwisu lokalizacji opcjonalnie (np. obsługa zdjęć z geotagami), do jednokrotnego pobrania wartości lub wymagać go do poprawnego działania (np. geotracking). Są możliwe dwa podejścia – synchroniczne i asynchroniczne.
Zacznijmy od omówienia aplikacji korzystającej opcjonalnie z lokalizacji uruchamiającej serwis w sposób asynchroniczny. W tym przypadku wykonujemy następujące kroki:
W projekcie dodajemy referencję do System.Device.dll.
Tworzymy instancję klasy GeoCoordinateWatcher. W parametrze konstruktora przekazujemy wymagany poziom dokładności. Jeśli nie potrzebujemy wysokiej precyzji, wybieramy wartość GeoPositionAccuracy.Default.
GeoCoordinateWatcher watcher;
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default); //Default, High
Za pomocą propercji MovementThreshold określamy minimalną zmianę pozycji, przy której wywoływane jest zdarzenie PositionChanged.
watcher.MovementThreshold = 20;
Ponieważ GPS w urządzeniach mobilnych nie ma anteny, sensory są zazwyczaj bardzo czułe. Może to spowodować pewną ilość szumów w sygnale, np. wskutek odbić od powierzchni. Ustawienie propercji MovementThreshold na bardzo małą wartość może spowodować, że aplikacja będzie otrzymywać zdarzenia w wyniku szumu. Aby w sygnale były tylko znaczące zmiany pozycji, powinniśmy ustawić wartość progową na co najmniej 20 metrów. Aplikacja będzie wówczas zużywać też mniej energii. Wartość 0 metrów może być przydatna w przypadku aplikacji nawigacyjnych.
Dodajemy handlery dla zdarzeń StatusChanged i PositionChanged:
watcher.StatusChanged += newEventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += newEventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
Aplikacja powinna być przygotowana na niedostępność serwisu lokalizacji. Za pomocą handlera zdarzenia StatusChanged możemy wykryć, czy serwis lokalizacji jest gotowy i czy zawiera dane. Stan serwisu może przyjąć jedną z wartości typu wyliczeniowego: GeoPositionStatus: Ready, Disabled, Initializing i NoData. W przypadku wartości Disabled powinniśmy sprawdzić wartość propercji Permission w klasie GeoCoordinateWatcher. Jeśli tą wartością jest Denied, oznacza to, że użytkownik wyłączył serwis lokalizacji w swoim telefonie. Aplikacja może mu wtedy wyświetlić komunikat o zaistniałej sytuacji.
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
string currentStatus = e.Status.ToString();
//...
}W handlerze zdarzenia PositionChanged otrzymujemy instancję klasy GeoCoordinate, która zawiera informacje na temat aktualnego położenia (szerokość i długość geograficzną, wysokość, kierunek, prędkość, dokładność w poziomie i w pionie). Niektóre z tych wartości mogą nie być dostępne (NaN). Zależy to od zastosowanej metody wyznaczania lokalizacji i wymaganej dokładności.
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (!e.Position.Location.IsUnknown)
{
double latitude = e.Position.Location.Latitude;
double longitude = e.Position.Location.Longitude;
double altitude = e.Position.Location.Altitude;
double course = e.Position.Location.Course;
double speed = e.Position.Location.Speed;
double hAccuracy = e.Position.Location.HorizontalAccuracy;
double vAccuracy = e.Position.Location.VerticalAccuracy;
DateTimeOffset time = e.Position.Timestamp;
//...
}
}Handlery do omówionych zdarzeń wywoływane są w innym wątku niż interfejs użytkownika. Musimy zadbać, aby kod aktualizujący UI był wywoływany w jego wątku.
Uruchamiamy instancję serwisu lokalizacji za pomocą asynchronicznej metody Start:
watcher.Start();
Gdy nie potrzebujemy już otrzymywać informacji o zmianach położenia, należy wyłączyć serwis za pomocą metody Stop.
Jeśli chcemy użyć serwisu lokalizacji do jednorazowego odczytu położenia, możemy pominąć obsługę zdarzenia PositionChanged, a w handlerze zdarzenia StatusChanged modyfikujemy kod:
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
if (e.Status == GeoPositionStatus.Ready)
{
double latitude = watcher.Position.Location.Latitude;
double longitude = watcher.Position.Location.Longitude;
//...
watcher.Stop();
}
}Przy statusie Ready odczytujemy informacje na temat położenia, korzystając z propercji Position klasy GeoCoordinateWatcher. Następnie zatrzymujemy serwis.
Jeśli chcemy w naszym kodzie poczekać na pierwszą wartość udostępnioną przez serwis lokalizacji, stosujemy podejście synchroniczne. Zamiast metody Start wołamy metodę TryStart i w jej drugim parametrze podajemy timeout. Pierwszy parametr nie jest używany w obecnej wersji serwisu.
watcher.TryStart(true, TimeSpan.FromSeconds(60));
Nawet jeśli serwis jest w stanie pozyskiwać dane, jego inicjalizacja i pierwszy odczyt następuje zazwyczaj po 15 sekundach, ale może się też przedłużyć do 120 sekund. Przy małej dokładności serwis nie czeka na dane z GPS. Będzie więc na ogół szybciej, jeśli GPS nie został poprzednio aktywowany. Jeśli inne aplikacje korzystają z GPS, który dostarcza im już dane, ustalenie położenia z wysoką dokładnością powinno nastąpić tak samo szybko lub szybciej niż z niską dokładnością.
Uruchomienie synchroniczne jest przydatne zarówno przy jednorazowym odczycie, jak i w aplikacjach wymagających lokalizacji do działania.
Przy jednorazowym odczycie nie korzystamy ze zdarzeń, a po metodzie TryStart możemy umieścić następujący kod:
if (watcher.Status == GeoPositionStatus.Ready && !watcher.Position.Location.IsUnknown)
{
double latitude = watcher.Position.Location.Latitude;
double longitude = watcher.Position.Location.Longitude;
//...
}watcher.Stop();
Klasa GeoCoordinateWatcher posiada propercję Status przyjmujacą jedną z wartości typu wyliczeniowego GeoPositionStatus. Przy wartości Ready możemy spróbować odczytać informacje na temat położenia.
W aplikacjach wymagających lokalizacji możemy sprawdzić, czy odczytywanie położenia zostało rozpoczęte w momencie ich startu. Jeśli metoda TryStart się powiodła i zwróciła wartość true, sprawdzamy, czy status serwisu ma wartość Ready. Reszta kodu aplikacji może być taka, jak w pierwszym przykładzie. Podobnie obsługujemy zdarzenia serwisu i zapewniamy możliwość jego wyłączenia.
Kod używający lokalizacji nie działa na emulatorze. Nie mamy emulacji odbiornika GPS, nie możemy korzystać z Wi-Fi ani sieci komórkowej. Niemniej jednak możemy stworzyć imitację serwisu lokalizacji. Implementację takiej funkcjonalności ułatwia biblioteka Reactive Extensions (Rx). Szczegóły znajdziesz w dokumentacji na stronie How to: Use Reactive Extensions to Emulate and Filter Location Data for Windows Phone.
Położenie geograficzne bardzo często wizualizuje się za pomocą mapy. W Windows Phone 7 można to zrealizować za pomocą kontrolki Map, o której możesz przeczytać na stronie Bing Maps Silverlight Control for Windows Phone.
Podsumowanie
W tym artykule zapoznaliśmy się z serwisem lokalizacji, jego architekturą i właściwościami. Wiemy, za pomocą jakich metod może on wyznaczać położenie. Potrafimy korzystać z lokalizacji w aplikacjach różnego rodzaju.