Статьи

Визуализация геозон в виде кругов на карте в Universal Apps

вступление

Если вы пытались работать с геозонами, возможно, вы столкнулись с этой проблемой. Вы хотите иметь простой способ показать, где эти чертовы вещи находятся на карте, чтобы вы могли протестировать приложение. На самом деле это довольно просто, опираясь на материал для рисования кругов на  Windows Phone  8, который я  написал ранее .

Что важно знать — теперь есть больше или универсально применимые объекты, чтобы изобразить местоположение. Первый —  BasicGeoposition  — это структура, которая в основном содержит только широту, долготу и высоту. Второй —  Geopoint . Вы создаете Geopoint из BasicGeoposition. Этот вид класса-обертки, который позволяет устанавливать некоторые дополнительные настройки местоположения — например, как следует интерпретировать координаты и высоту. Я не думаю, что что-то из этого действительно используется, но у меня есть ощущение, что со временем Geopoint станет более важным, и в любом случае, как ГИС, я склонен использовать самый полный набор обозначений местоположения. Но вы также можете просто использовать BasicGeoposition для передачи координат в универсальном приложении.

Делать математику!

По сути, это тот же метод, который я написал ранее, но адаптированный для Geopoint. Некоторые другие изменения произошли от моего бывшего коллеги  Ярно Пескье , который в своем типичном виде видел возможности оптимизировать ускорение кода вычисления круга (и оптимизация действительно работает — я проверял).

using System;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class GeopointExtensions
  {
    private const double circle = 2 * Math.PI;
    private const double degreesToRadian = Math.PI / 180.0;
    private const double radianToDegrees = 180.0 / Math.PI;
    private const double earthRadius = 6378137.0;
    
    public static IList<Geopoint> GetCirclePoints(this Geopoint center,
                                   double radius, int nrOfPoints = 50)
    {
      var locations = new List<Geopoint>();
      double latA = center.Position.Latitude * degreesToRadian;
      double lonA = center.Position.Longitude * degreesToRadian;
      double angularDistance = radius / earthRadius;

      double sinLatA = Math.Sin(latA);
      double cosLatA = Math.Cos(latA);
      double sinDistance = Math.Sin(angularDistance);
      double cosDistance = Math.Cos(angularDistance);
      double sinLatAtimeCosDistance = sinLatA * cosDistance;
      double cosLatAtimeSinDistance = cosLatA * sinDistance;

      double step = circle / nrOfPoints;
      for (double angle = 0; angle < circle; angle += step)
      {
        var lat = Math.Asin(sinLatAtimeCosDistance + cosLatAtimeSinDistance * 
                            Math.Cos(angle));
        var dlon = Math.Atan2(Math.Sin(angle) * cosLatAtimeSinDistance, 
                              cosDistance - sinLatA * Math.Sin(lat));
        var lon = ((lonA + dlon + Math.PI) % circle) - Math.PI;

        locations.Add(new Geopoint(new BasicGeoposition { 
        Latitude = lat * radianToDegrees, Longitude = lon * radianToDegrees }));
      }
      return locations;
    }
  }
}

Я думаю, что это  формула Хаверсайна,  но я не уверен, и суть в том, что она дает список точек, которые при рисовании на карте дают разумное приближение круга, где бы вы его ни нарисовали. И вот о чем это. Радиус — это радиус в метрах, а numberOfPoints — это количество точек, которые используются для рисования многоугольника. Ибо это то, что мы делаем — пока будет достаточно очков, наши глаза увидят это как круг.

Этот метод реализован как метод расширения для Geopoint, и пока я работал над этим, я добавил вспомогательный метод и для BasicGeoposition, что было не совсем ракетостроением.

public static IList<Geopoint> GetCirclePoints(this BasicGeoposition center,
                              double radius, int nrOfPoints = 50)
{
  return new Geopoint(center).GetCirclePoints(radius, nrOfPoints);
}

Геозоны ты сказал сэр?

До сих пор я бормотал только о координатах — о чем я часто говорю, но как мне перейти от геозон к вещам на карте? Ну, довольно легко, на самом деле. Оказывается, у геозоны есть свойство Geoshape типа  IGeoshape , но поскольку геозона прямо сейчас может быть только кругом, вы можете попытаться привести его к Geocircle . А Geocircle имеет свойство Center типа BasicGeoposition и свойство Radius в метрах. И это то, что нам нужно было использовать метод расширения GetCirclePoints. Так что на самом деле довольно легко определить новый метод расширения

using System.Collections.Generic;
using Windows.Devices.Geolocation.Geofencing;
using Windows.Devices.Geolocation;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceExtensions
  {
    public static IList<Geopoint> ToCirclePoints(this Geofence fence, 
      int nrOfPoints = 50)
    {
      var geoCircle = fence.Geoshape as Geocircle;

      if (geoCircle != null)
      {
        return geoCircle.Center.GetCirclePoints(geoCircle.Radius, nrOfPoints);
      }
      return null;
    }
  }
}

Я всегда делаю проверку как-и-если-ноль. Я мог бы сделать жесткий бросок, но я все для защитного программирования.

Теперь дело в том, что  GeofenceMonitor  имеет коллекцию Geofences, которую можно использовать для добавления геозон (duh), но, конечно, вы также можете получить их. И, таким образом, вы можете сделать еще один простой метод расширения, чтобы получить список точек вашего текущего GeoFenceMonitor

using System.Collections.Generic;
using System.Linq;
using Windows.Devices.Geolocation;
using Windows.Devices.Geolocation.Geofencing;

namespace MappingUtilities.Geofencing
{
  public static class GeofenceMonitorExtensions
  {
    public static IList<IList<Geopoint>> GetFenceGeometries(this GeofenceMonitor monitor)
    {
      return monitor.Geofences.Select( p=> p.ToCirclePoints()).ToList();
    }
  }
}

А поскольку GeofenceMonitor является статическим классом с одноэлементным экземпляром, получение всех точек всех геозон в вашем приложении в удобном списке одним простым вызовом выглядит следующим образом:

GeofenceMonitor.Current.GetFenceGeometries();

И перебирая их, вы можете рисовать фигуры на карте. Самое интересное, что теперь у нас есть Universal Apps, вы можете определить весь этот код в PCL и использовать его как в Windows Phone, так и в Windows, и ссылаться на него как на двоичный. Cегодня. Не нужно ждать Windows 10. Поэтому я поместил эти методы в PCL под названием MappingUtilities. А поскольку текущий GeofenceMonitor является одноэлементным, вы можете использовать этот код в каком-либо методе кнопок в своем приложении, чтобы просто получить быстрый список активных геозон и нарисовать их на карте, без необходимости извлекать данные из ваших моделей или моделей представления, или однако вы решите реализовать свою логику.

Но есть маленькая деталь. Bing Maps не использует Geopoint или BasicGeoposition, но все же класс Location, которого нет в Windows Phone.

Еще одна вещь, чтобы продлить

Так как я все равно создавал методы расширения, я сделал новую сборку, на этот раз специфичную для Windows, под названием MappingUtilities.Windows — держу пари, вы не ожидали, что это произойдет 🙂 — и добавил следующие два метода расширения:

using System.Linq;
using Bing.Maps;
using System.Collections.Generic;
using Windows.Devices.Geolocation;

namespace MappingUtilities
{
  public static class PointHelpers
  {
    public static LocationCollection ToLocationCollection(
      this IList<Geopoint> pointList)
    {
      var locations = new LocationCollection();
      foreach (var p in pointList)
      {
        locations.Add(p.ToLocation());
      }
      return locations;
    }

    public static Location ToLocation(this Geopoint location)
    {
      return new Location(location.Position.Latitude, 
                          location.Position.Longitude);
    }
  }
}

И это вы можете просто использовать, чтобы быстро превратить Geopoint в Location, или список Geopoints в LocationCollection. Как это используется, показано в  демонстрационном решении .

Завершение

Демонстрационное решение  создает две геозоны и показывает, как довольно легко можно отобразить расположение упомянутых заборов, вызвав метод расширения GetFenceGeometries как в Windows Phone, так и в Windows.

образобраз

Рисование на карте выполняется простым методом, который я не буду здесь вдаваться. Я хотел бы подчеркнуть тот факт, что создание геозон находится в общей части приложения, а также тот факт, что эти геозоны довольно минимальны — я просто создаю их с настройками по умолчанию — и что я ничего не делаю  с  эти геозоны отдельно от их создания. Единственное, что показывает это приложение, это то, насколько легко отображать геозоны с помощью методов расширения, если у вас  есть  геозоны.

Если вы хотите узнать больше об использовании геозон в универсальных приложениях и о том, как вы могли бы на самом деле делать  с ними что-то  полезное , пожалуйста, посетите мой доклад 18 октября 2014 года, где я буду проводить сеанс на эту тему в  день хакера Lowland для Windows Phone . Этот день организован совместными усилиями голландских и бельгийских сообществ разработчиков в Тилбурге, Нидерланды, в непосредственной близости от бельгийско-голландской границы. Помимо отличного контента — у нас также есть несколько призов, которые могут вас заинтересовать

образ

Просто говорю 🙂