Проработав в географических информационных системах более 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 безумно просто.
Полный исходный код, как обычно, можно скачать здесь .