company_banner

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


    Знакомство начнём с элемента управления 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 на русском языке
    Microsoft
    195.52
    Microsoft — мировой лидер в области ПО и ИТ-услуг
    Share post

    Similar posts

    Comments 21

      +2
      Ух, как подробно. Спасибо!
        0
        А почему все умалчивают про размещение Map, не на первой странице приложения?
        Кстати пример на msdn, мне кажется, подробнее, он еще показывает как области обводить.
          +1
          Потому что в рамках примера использования технологии, нет разницы на какой странице элемент управления находится.

          Вполне соглашусь с тем, что MSDN — хороший источник примеров.
            +1
            А вот разница то как раз есть. Вы попробуйте.
              0
              Я знаю, что разница есть.

              Попытаюсь ответить ещё раз, но более развёрнуто. В рамках моего желания продемонстрировать именно технологическую возможность, использование второй страницы, потребует дополнительных объяснений и отвлечёт от основной темы.
                0
                А можете в кратце описать в чем разница? Я думаю рано или поздно разработчикам все равно прийдеться столкнуться с этой проблемой.
                  +1
                  Я бы не называл это проблемой :).

                  Я не знаю, какую именно разницу имеет ввиду коллега. Но я расскажу, какую разницу я имею ввиду.

                  Сначала вводные.

                  1. Элемент управления Map — это интерфейс к веб-сервису карт bing, т.е. для отображения карт он открывает сетевое соединение и общается с веб-сервисом.
                  2. Концепция страниц в приложениях Windows Phone использует прадигму веб-страниц.

                  Т.о. если вы просто напишите приложение, которое будет иметь основной страницей пустую страницу со мышкой на вторую страницу, а второй странице, страницу с картой которая используется в примере и вся работа будет происходить на ней, разницы вы не увидите.

                  Если вы будете использовать варианты разметки pivot и panorama, вас может удивить, что там считается страницей и как загружается :) И могут возникнуть вопросы с инициализацией элементов.

                  При желании «взаимодействовать» с картой на другой странице со своей, с учётом вышеописанного, вам может понадобиться решить ряд задач, которые не связаны с технологией, но связаны с пониманием устройства приложения в Windows Phone и компонента Map.

                  Поэтому я в примере использую простое приложение с одной страницей — этого достаточно, чтобы понять возможности технологии. Если необходимо продвинутое взаимодействие с картами bing, ниже я давал ссылку на англоязычное руководство, где можно почитать подробнее про возможности использования карт.
                    0
                    Ок, спасибо! Пример опробовал и, вроде как, с простыми страницами проблем никаких нет. И благодарю за хороший пример.
                    0
                    Мы столкнулись немного с другой проблемой, так если в программе есть хотя бы один дополнительный поток кроме главного (в нашем случае это был сборщик данных с аудиоджека) объект карты не активировал поток, собирающий тайлы с сети (в случае если карты не на главной странице). Если дополнительных потоков нет, то все работает.
                      0
                      На мой взгляд, моё описание «разницы», как раз и соответсвует вашей ситуации.

                      Как я понимаю, вы ожидали, что на неактивной странице (недеюсь, вы не имеете ввиду PivotItem или PanoramaItem), расположенный на ней компонент Map, будет открывать соединение и активно общаться с сетью.

                      В моей логике, логично, что он этого не делает.
                      0
                      Конкретно я столкнулся с тем, что контрол на первой странице приложения работает нормально, если же размещать его на второй странице, то в эмуляторе отваливается масштабирование и позиционирование, т.е. карту можно перемещать из кода, но та пальцы она не реагирует, после развертывания на живом аппарате, на первой странице все так же ок, на второй масштабирование есть, позиционирования нет.
                      Я имею ввиду именно разные страницы, запихнуть карту в pivot или панораму это извращение над пользователем.
                        0
                        А можно мне Proof of Problem, т.е. демо-проект с описанием, что мне нужно сделать, чтобы было нехорошо. Если я удостоверюсь, что это ошибка/проблема, проэскалирую её разработчикам. По крайней мере попытаюсь это сделать.

                        Лучше выложить куда-нибудь архив с поектом, или по e-mail stas.pavlov@горячая_почта.ком
                          0
                          Дома накидаю, но в принципе ничего особого я не извращался. Просто приложение, 2 страницы, на одной карта и на другой. на первое еще ссылка на вторую страницу.
                            0
                            Можно было бы попросить просто сценарий, но не хотелось бы эффекта испорченного телефона.
              +4
              Как же просто оказывается писать под WP. Спасибо за пример.
                +1
                Что мне нравится в phone 7 это то, что приложения даже в самом простом своём варианте выглядят красиво. На андроиде стандартные контролы страшны чуть менее чем полностью.
                  0
                  Winphone-вские пины страшнее андроидных. Напишите как заменить на свои картинки.
                    0
                    Это стандартный Pushpin без текста и т.д. Т.к. это стандартный элементы управление, то на него можно «натянуть» любой шаблон. См. msdn.microsoft.com/en-us/gg266447
                    +1
                    Спасибо за статью.
                    Небольшое, но важное замечание (хоть и не связанное с основной темой) — использовать new Thread(..).Start() очень-очень не рекомендуется. Посмотрите в сторону ThreadPool.QueueUserWorkItem или System.Threading.Tasks (TPL на wp7 недоступен «из коробки», но есть хороший порт с Mono)
                      0
                      Хорошее замечание. Дописал кусочек в статью.
                      0
                      Замечательная статья.
                      Вот еще одна ссылка: Визуализация маршрутов Bing в Windows Phone 7

                      Only users with full accounts can post comments. Log in, please.