Pull to refresh
0
Microsoft
Microsoft — мировой лидер в области ПО и ИТ-услуг

Карты и гелокационные данные на Windows Phone

Reading time7 min
Views3.8K

Знакомство начнём с элемента управления Map, который предоставляет интерфейс к картам на Windows Phone. Давайте создадим новый проект из шаблона Windows Phone Application и назовём его ExploreMapControl.

После того, как проект будет создан, посмотрите, на какие библиотеки он ссылается.

Далее, разверните Toolbox, если он свёрнут, и перетяните элемент управления Map в дизайнер интерфейса приложения.



Обратите внимание, что теперь проект ссылается на Microsoft.Phone.Controls.Map.



Двойным щелчком перейдем к странице MainPage.xaml и посмотрим, что изменилось в XAML коде. Добавился элемент управления Map из пространства имён my:
<my:Map Height="50" HorizontalAlignment="Left" Margin="201,198,0,0" Name="map1" VerticalAlignment="Top" Width="100" />

Посмотрев на заголовок XAML документа можно увидеть, что это за пространство имён:
xmlns:my="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"

Давайте заменим my на map, чтобы название пространства имён соответствовало его содержанию:
xmlns:map="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"


<map:Map Height="50" HorizontalAlignment="Left" Margin="201,198,0,0" Name="map1" VerticalAlignment="Top" Width="100" />

Отредактируем XAML код элемента управления Map или воспользуемся панелью Properites так, чтобы элемент занимал большую часть свободного пространства и переименуем элемент в MyMap:
<map:Map  Name="MyMap"/>


Запустите приложение (F5) и посмотрите, как выглядит элемент управления во время исполнения.



Обратили внимание на белый баннер в центре экрана, который говорит, что у нас неправильные авторизационные данные? Это потому что этот элемент управления использует сервис карт от Bing и для его использования требуется регистрация. Зарегистрироваться и получить ключ можно на портале Bing Maps: http://www.bingmapsportal.com.
В завершение регистрации разработчик получает строковый ключ, который надо указать в свойстве CredentialsProvider элмента управления, также его можно вынести в ресурсы или данные.
Добавим простые элементы управления картой: уменьшение/увеличение масштаба, смена режима отображения карты.

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   <map:Map Name="MyMap">
      <Button Name="ZoomIn" Content="+" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="-100,0,0,-5" Click="ZoomIn_Click" FontWeight="Bold" Padding="0,-9,0,0"/>
      <Button Name="ZoomOut" Content="-" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="100,0,0,-5" Click="ZoomOut_Click" FontWeight="Bold" Padding="0,-9,0,0" />        
      <Button Name="LayoutChange" Content="L" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="0,0,0,-5" FontWeight="Bold" Padding="0,-9,0,0" Click="LayoutChange_Click"/>
    </map:Map>
</Grid>

И обработаем эти события в коде приложения.
private void ZoomIn_Click(object sender, RoutedEventArgs e)
{
     MyMap.ZoomLevel += 1; 
}
 
private void ZoomOut_Click(object sender, RoutedEventArgs e)
{
     MyMap.ZoomLevel -= 1; 
}
 
private void LayoutChange_Click(object sender, RoutedEventArgs e)
{
    if (MyMap.Mode is RoadMode)
    {
         MyMap.Mode = new AerialMode(true);
    }
    else
    {
         MyMap.Mode = new RoadMode();
    }         
}

Не забудьте добавить в блок using следующую директиву:
using Microsoft.Phone.Controls.Maps;

Запустите приложение (F5) и проверьте, что наши элементы управления работают, как предполагается. В соответствии с Metro-дизайном панель с кнопками внизу окна приложения мы должны были бы оформить в виде Application Bar, только в целях упрощения примера мы используем более простой вариант. В качестве самостоятельного упражнения можете попробовать убрать кнопки, раскоментировать пример кода Application Bar в XAML файле и переделать приложение в соответствии со стилем Metro.

Теперь перейдём к геолокационным сервисам, доступным на телефоне. Сервис предоставляет информацию, используя комбинацию информации получаемой от Wi-Fi, сотовой связи и данных от GPS приёмника. Добавим теперь в наще приложение возможности предоставляемые сервисами геолокации.

Для начала, добавим в блок using следующую директиву:
using Microsoft.Devices.Sensors;

Теперь мы готовы работать с сервисами локаций/местоположения.

Сначала напишем простое дополнение к нашей программе, которое будет центрировать карту в соответствии с гелолокационными данными, полученными от сервисов.
Добавим в класс определение переменной типа GeoCoordinateWatcher, которая позволит нам инициализировать сервисы геолокации и полчать от них данные.
private GeoCoordinateWatcher myGeoWatcher;

В конструктор класса, сразу же после кода относящегося к акселерометру добавим код инициализации и регистрации на события изменения статуса сервисов (они могут быть недоступны, могут быть не готовы и т.д.) и события изменения положения.
yGeoWatcher = new GeoCoordinateWatcher();
myGeoWatcher.MovementThreshold = 100.0f;
 
myGeoWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(myGeoWatcher_StatusChanged);
 
myGeoWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(myGeoWatcher_PositionChanged);

Хорошее приложение должно правильно обрабатывать статусы геосервисов, т.к. они не всегда могут выдавать данные и могут тратить достаточно большое время на инициализацию. Для начала мы просто оставим обработчик пустым, так как тестировать приложением мы будем на эмуляторе, и там эти проблемы отсутствуют.
Также правильнее будет поместить запуск сервиса геолокации в отдельный поток, чтобы не тормозить загрузку приложения, в нашем первом варианте приложения, с учётом использования эмулятора мы пока будем запускать сервис прямо в конструкторе класса:
myGeoWatcher.TryStart(false, TimeSpan.FromSeconds(60));

Если Visual Studio автоматически сгенерировала нам обработчики событий StatusChanged и PositionChanged, закомментируйте или сотрите код в этих методах, вызывающий исключение NotImplemented:
throw new NotImplementedException();

В обработчик события PositionChanged добавьте код, центрирующий карту при изменении позиции:
void myGeoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
     MyMap.Center = e.Position.Location;
}


Запустите приложение (F5) и воспользуйтесь возможностями эмулятора по эмуляции геолкационных данных, чтобы проверить работу программы. Увеличьте масштаб так, чтобы убедиться, что позиционирование происходи правильно.


Следующим шагом, улучшения нашей программы может стать запуск сервисов в другом потоке, добавление строки статуса геолокационных данных, а также создание на карте точки, отмечающей наше местоположение.
Для использования потоков, добавим в блок using следующую директиву:
using System.Threading;

В конструкторе до запуска сервисов добавим код:
new Thread(startMyGeoWotcher).Start();

После этого создадим функцию, не принимающую и не возвращающую значений, с именем startMyGeoWotcher и перенесём в неё код запуска сервисов:
void startMyGeoWotcher()
{
     myGeoWatcher.TryStart(false, TimeSpan.FromSeconds(60));
}

Теперь добавим элемент управление TextBlock для отображения статуса сервисов геолокации

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   <StackPanel>
     <TextBlock Name="GeoStatus" HorizontalAlignment="Center" VerticalAlignment="Top" Text="Geo Status .."  />
       <map:Map Name="MyMap" Height="580">           
          <Button Name="ZoomIn" Content="+" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="-100,0,0,-5" Click="ZoomIn_Click" FontWeight="Bold" Padding="0,-9,0,0"/>
          <Button Name="ZoomOut" Content="-" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="100,0,0,-5" Click="ZoomOut_Click" FontWeight="Bold" Padding="0,-9,0,0" />
           <Button Name="LayoutChange" Content="L" HorizontalAlignment="Center" VerticalAlignment="Bottom" Height="60" Width="60" Margin="0,0,0,-5" FontWeight="Bold" Padding="0,-9,0,0" Click="LayoutChange_Click"/>
         </map:Map>
    </StackPanel>
</Grid>

И допишем вывод статусов в обработчик StatusChanged:
void myGeoWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
    switch (e.Status)
   {
       case GeoPositionStatus.Disabled:
          if (myGeoWatcher.Permission == GeoPositionPermission.Denied)
          {
               GeoStatus.Text = "Сервис выключен";
          }
           else
           {
               GeoStatus.Text = "На этом устройстве сервис недоступен";
           }
           break;
        case GeoPositionStatus.Initializing:
            GeoStatus.Text = "Сервис инициализируется";
            break;
         case GeoPositionStatus.NoData:
             GeoStatus.Text = "Данные о месположении недоступны";
             break;
          case GeoPositionStatus.Ready:
              GeoStatus.Text = "Данные о местоположении доступны";
              break;
      }         
}

Наконец, в обработчик события изменения позиции добавим установку точки на карте.
В класс добавим переменную типа Pushpin:
private Pushpin myPushpin;

Создадим её в конструкторе класса:
myPushpin = new Pushpin();

В обработчике изменения позиции установим её на текущую позицию и добавим на карту, если её там нет:
myPushpin.Location = e.Position.Location;
 
if (!MyMap.Children.Contains(myPushpin)) MyMap.Children.Add(myPushpin);

Запустите приложение (F5) и воспользуйтесь возможностями эмулятора по эмуляции геолкационных данных, чтобы проверить работу программы. Увеличьте масштаб так, чтобы убедиться, что позиционирование и установка точки происходит правильно; также проверьте отображаемый статус сервисов геолокации.


UPD: Как правильно написал в коментариях andrew_kane. Работать с потоками из пула — это более правльная практика.

Ниже привожу код, который необходимо поменять, чтобы работать с потоками из пула, а не вызывать
new Thread(...).Start();

Вместо
new Thread(startMyGeoWotcher).Start();

нужно написать следующий код:
ThreadPool.QueueUserWorkItem(startMyGeoWotcher, myGeoWatcher);

Мы вынуждены передвать объект в метод, потому что использование пула требует статическогом метода и у нас есть 2 варианта, либо делать объект статическим, либо передать его в качестве параметра.

Теперь нужно переписать метод
startMyGeoWotcher
Он должен теперь выглядеть следующим образом:
static void startMyGeoWotcher(object GeoWatcher)
{
     ((GeoCoordinateWatcher)GeoWatcher).TryStart(false, TimeSpan.FromSeconds(60));
}


Полезные ссылки:
Центр разработки Windows Phone на MSDN
Windows Phone SDK 7.1
Форумы по разработке под Windows Phone на русском языке
Tags:
Hubs:
+27
Comments21

Articles

Change theme settings

Information

Website
www.microsoft.com
Registered
Founded
Employees
Unknown
Location
США