Проработав в географических информационных системах более 20 лет, я могу с полным основанием сказать, что новых картографических возможностей и возможностей определения местоположения в Windows Phone 8 более чем достаточно, чтобы сделать карту маньяка, похожего на меня, с блестящими глазами. У него есть возможности, которые не слышали даже пару лет назад — и мне не нужна большая рабочая станция, мне даже не нужен ПК — он работает на моем телефоне. Мир в моем кармане — в самом буквальном смысле этого слова.
Два популярных приложения ГИС — геокодирование и обратное геокодирование . Геокодирование позволяет вам найти положение Земли для описательного текста — например, адреса, города, названия здания или любой другой фразы, указывающей место на Земле. Обычно это довольно просто. Обратное геокодирование с точностью до наоборот — это «что здесь?» вопрос — учитывая местоположение, что я здесь нахожу? Кстати, отвечая на такие вопросы, я зарабатываю на жизнь в Викрее .
Windows Phone 8 делает обратное геокодирование практически невероятно простым. Даже при использовании MVVMLight . Поэтому я сделал простое приложение, которое показывает адреса, найденные в том месте, где вы нажимаете на карте.
Мы начнем с простой модели с двумя свойствами:
using System.Collections.ObjectModel; using System.Device.Location; using System.Linq; using GalaSoft.MvvmLight; using Microsoft.Phone.Maps.Services; namespace TapReverseGeocode.Logic.ViewModels { public class MapViewModel : ViewModelBase { public MapViewModel() { Addresses = new ObservableCollection<string>(); } private GeoCoordinate tapCoordinate; public GeoCoordinate TapCoordinate { get { return tapCoordinate; } set { tapCoordinate = value; RaisePropertyChanged(() => TapCoordinate); StartReverseGeoCoding(); } } public ObservableCollection<string> Addresses { get; set; } } }
ObservableCollection «Адреса» будет содержать результаты, и, как обычно, при связывании с ObservableCollection вы должны убедиться, что он инициализируется раньше всего — конструктор является хорошим местом для этого. Дизайнер может связать это с каким-то элементом графического интерфейса, который отображает результат.
Свойство TapCoordinate является GeoCoordinate, которое запускает реальное обратное геокодирование — и я специально пропустил обычную проверку « if (viewModelPropertyName! = Value) ». Даже когда пользователь дважды нажимает на одно и то же местоположение, я хочу, чтобы код обратного геокодирования срабатывал каждый раз.
Код, который запускает обратное геокодирование, сам по себе не является ракетостроением:
private void StartReverseGeoCoding() { var reverseGeocode = new ReverseGeocodeQuery(); reverseGeocode.GeoCoordinate = new GeoCoordinate(TapCoordinate.Latitude, TapCoordinate.Longitude); reverseGeocode.QueryCompleted += ReverseGeocodeQueryCompleted; reverseGeocode.QueryAsync(); }
Чтобы предотвратить условия гонки, я создаю новую GeoCoordinate из предоставленной пользователем, настраиваю обратный вызов и запускаю асинхронный запрос.
Последний кусок — это простой обратный вызов, который обрабатывает результат обратного геокодирования.
private void ReverseGeocodeQueryCompleted(object sender, QueryCompletedEventArgs<System.Collections.Generic.IList<MapLocation>> e) { var reverseGeocode = sender as ReverseGeocodeQuery; if (reverseGeocode != null) { reverseGeocode.QueryCompleted -= ReverseGeocodeQueryCompleted; } Addresses.Clear(); if (!e.Cancelled) { foreach (var adress in e.Result.Select(adrInfo => adrInfo.Information.Address)) { Addresses.Add(string.Format("{0} {1} {2} {3} {4}", adress.Street, adress.HouseNumber, adress.PostalCode, adress.City,adress.Country).Trim()); } } }
Он очищает обратный вызов, очищает список адресов, а затем обрабатывает части результата в одну строку для каждого адреса. Как и в любом хорошем сервисе обратного геокодирования, Microsoft реализовала это для получения набора результатов — в одном месте может быть больше адресов, например, в большом жилом доме, — хотя я никогда не получал более одного результата на каждое местоположение, когда я проверял это в Нидерланды.
Это полная модель обратного геокодирования, которая, в принципе, не заботится о том, откуда берется GeoCoordinate или куда идет результат. Так что это очень универсально. Там нет никакого графического интерфейса, и все же у нас уже есть работающее приложение
Исходный XAML для связывания этого материала — после установки текста данных для этой модели представления — выглядит довольно просто:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <maps:Map/> <Grid Height="58" VerticalAlignment="Top" Background="#7F000000"> <phone:LongListSelector ItemsSource="{Binding Addresses}" HorizontalContentAlignment="Left" Margin="12,0"/> </Grid> </Grid>
… и затем мы сталкиваемся с проблемой. Два на самом деле. Последнее нажатое местоположение не является свойством, которое мы можем привязать , и что это местоположение — Точка — местоположение на экране , а не GeoCoordinate в координатах реального мира.
Это может быть решено с помощью свойства Attached Dependency Property (я думаю), с помощью некоторого кода позади или моего товарного знака — путем создания простого поведения. В конце концов, я не хочу беспокоить дизайнеров кодом, и мне нравится простота повторного использования поведения:
using System.Device.Location; using System.Windows; using Microsoft.Phone.Maps.Controls; using Wp7nl.Behaviors; namespace Wp8nl.Behaviors { public class TapToCoordinateBehavior : SafeBehavior<Map> { protected override void OnSetup() { AssociatedObject.Tap += AssociatedObjectTap; } void AssociatedObjectTap(object sender, System.Windows.Input.GestureEventArgs e) { var tapPosition = e.GetPosition((UIElement)sender); TappedCoordinate = AssociatedObject.ConvertViewportPointToGeoCoordinate(tapPosition); } protected override void OnCleanup() { AssociatedObject.Tap -= AssociatedObjectTap; } // GeoCoordinate TappedCoordinate dependency property omitted } }
Это поведение реализовано как дочерний класс SafeBehavior , чтобы предотвратить утечки памяти. На самом деле все довольно просто — он перехватывает событие «Tap», определяет местоположение, преобразует его в GeoCoordinate и помещает его в свойство зависимостей TappedCoordinate. Который, в свою очередь, может быть привязан к представлению модели. Дизайнер может просто перетащить это поведение поверх карты и настроить привязку данных. Разве ты не любишь смесь? XAML взять 2:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <maps:Map> <i:Interaction.Behaviors> <Behaviors:TapToCoordinateBehavior TappedCoordinate="{Binding TapCoordinate, Mode=TwoWay}"/> </i:Interaction.Behaviors> </maps:Map> <Grid Height="58" VerticalAlignment="Top" Background="#7F000000"> <phone:LongListSelector ItemsSource="{Binding Addresses}" HorizontalContentAlignment="Left" Margin="12,0"/> </Grid> </Grid>
И это все, что нужно сделать. Обратное геокодирование в Windows Phone 8 безумно просто.
Полный исходный код, как обычно, можно скачать здесь .