В Compass мы используем API Карт Google для поиска и отображения данных о недвижимости. Ключевым преимуществом Google Maps перед другими API-интерфейсами карт является просмотр улиц, что особенно важно в сфере недвижимости.

Поскольку мы уже несколько лет используем Карты Google в Интернете и на iOS, мы хотели бы поделиться некоторыми передовыми практиками использования этого API, которые помогут вашей команде создавать более эффективные приложения, использующие карты. Типы проблем, с которыми мы столкнулись, применимы ко многим объектам, связанным с путешествиями и недвижимостью, а также к другим, требующим высокоинтерактивных карт.

Исследовать

Нашим первым подходом к использованию Google Maps было посмотреть, не создал ли кто-нибудь Angular компоненты, которые обертывают Maps API, поскольку мы не хотим изобретать все заново. К сожалению, Google не выпустил собственную оболочку для Google Maps с Angular, поэтому мы рассмотрели варианты с открытым исходным кодом. Основными соперниками здесь являются angular-google-maps и ng-map, и существуют варианты этих библиотек для большинства основных интерфейсных фреймворков (React, Angular 2 и т. Д.).

После изучения двух упомянутых библиотек и рассмотрения других вариантов использования карт, общий набор функций, предлагаемых существующими библиотеками, следующий:

  • Асинхронный способ загрузки скрипта Google Maps API
  • Компонент для визуализации карты с масштабированием, центром и стилем.
  • Способ визуализации одного или нескольких маркеров на карте, обычно в состоянии щелчка.
  • Некоторые дополнительные функции, обычно включающие дополнительные библиотеки Карт, такие как предложения мест, геокодирование и т. Д.

В общем, мы обнаружили, что сбивает с толку то, что каждая из этих библиотек оборачивает Google API в свой собственный набор API, который отличается от того, что говорится в документации Google, а это означает, что вам нужно изучить оба API. На самом деле это не упрощает вашу работу с картами, поэтому мы не рекомендуем использовать библиотеку-оболочку для Google Maps.

Даже если дизайн этих библиотек был правильным, мы не могли использовать оболочку, потому что нам также необходимы:

  • Пользовательские кнопки в верхней части карты
  • Просмотр улиц
  • Наложения карт с привязками Angular
  • Рисование и редактирование фигур на карте
  • Использование на стороне клиента других сервисов Google, таких как геокодирование и предложения мест

Поэтому на основе всего этого мы решили написать свои собственные обертки и рекомендуем вам тоже.

Создание повторно используемых компонентов для каждого типа карты

В Compass мы используем множество видов карт, и каждая из них создается как отдельный компонент, который можно использовать по-разному. Так, например, у нас есть:

  • Стилизованные карты с одним стилизованным маркером
  • Карты с кластерными данными листинга
  • Статическая карта с множеством маркеров
  • Просмотр улиц

У каждого компонента есть API, подходящий для конкретного случая использования, с максимально возможным общим внутренним кодом. Мы обнаружили, что это правильный уровень абстракции, потому что он позволяет нам повторно использовать карты и связанные с ними инструменты в разных частях нашего приложения, а также позволяет нам быстро разрабатывать новые способы использования географической информации.

Асинхронная загрузка Google Maps API

Это функция, которую предлагают многие библиотеки, и мы также сочли ее полезной. Мы загружаем API Карт только тогда, когда вы переходите на экран, который его использует, а это не везде в нашем приложении. Это сначала делает наше приложение более отзывчивым и делает его пригодным для модульного тестирования.

Асинхронная загрузка может добавить много шаблонного кода, ожидающего загрузки скрипта. Чтобы избежать этого шаблона, мы используем наш маршрутизатор (ui-router) для управления загрузкой асинхронных сценариев. Для нас это выглядит так:

resolve: {
  googleMaps: (ucGoogleMapsApiService) => {
    return ucGoogleMapsApiService.load();
  },
},

Два способа добавления кнопок на карту

Использование элемента с абсолютным позиционированием поверх карты

Это вариант, который проще всего интегрировать в ваше приложение, где вы пишете свой пользовательский интерфейс, используя те же инструменты, которые обычно используете, и визуализируете элементы поверх карты. Мы выбрали эту опцию для простых кнопок «закрыть», а также для нашей самой сложной функции карты - поиска пользовательских границ. Мы выбираем этот вариант в большинстве случаев.

Pro:

  • Совершенно не использует API карт
  • Легко добавить условную логику и стили

Против:

  • Не интегрирован напрямую в карту

Интеграция кнопок в карту

Иногда мы хотим расположить наш пользовательский интерфейс так, как если бы он был частью самой карты, например, рядом с элементами управления масштабированием или элементами управления спутниковым обзором. Требуется больше работы, чтобы подключить всю логику обработки событий и рендеринга, которая требуется API, но она хороша для замены пользовательского интерфейса по умолчанию вашим собственным. Это можно сделать с помощью настраиваемых элементов управления:

Pro:

  • Взаимодействует со встроенными элементами управления картой, такими как масштабирование

Против:

  • Сложнее стилизовать
  • Сложнее добавить условную логику

Просмотр улиц

Когда мы думаем о просмотре улиц, обычно мы думаем о том, что происходит на Google Maps, когда вы вводите адрес в их строку поиска. Вы получаете предварительный просмотр просмотра улиц, указывающий прямо на введенный вами адрес. К сожалению, API просмотра улиц не позволяет легко получить аналогичный тип просмотра улиц, который вы получаете в собственных приложениях Google.

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

  • никогда не используйте запрос просмотра улиц по умолчанию, и
  • всегда направляйте камеру в желаемое место из центра панорамы

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

// Assumption: you have a map already
const streetView = map.getStreetView();
// Set location to be the lat/lng you want a street view for, more accurate is better
const location = map.getCenter();
const streetViewService = new maps.StreetViewService();
streetViewService.getPanorama({
  // Set location to whatever you want
  location,
  // Usually the NEAREST panorama matches Google Maps better than the BEST panorama, aka we
  // are looking for a straight-on view of a building, and BEST often returns a steeper
  // angle than NEAREST
  preference: maps.StreetViewPreference.NEAREST,
  // We do not want indoor street view
  source: maps.StreetViewSource.OUTDOOR,
}, (pano, status) => {
  if (status === 'OK') {
    // Pano ID is a session-stable reference to the queried street view, used for the static
    // and dynamic street view lookup
    const panoId = pano.location.pano;
    // The default heading of street view is north, we point it to the actual location
    const heading = maps.geometry.spherical.computeHeading(pano.location.latLng, location);
streetView.setPano(panoId);
    streetView.setPov({
      heading,
      pitch: 0,
    });
  } else {
    // Failure case, this is where you could show an error or hide street view
  }
});

Пользовательские наложения внутри карты с привязками Angular

API Карт имеет ограниченные способы создания интерактивных маркеров карты, которые используются на большинстве веб-сайтов о недвижимости и путешествиях. К счастью, есть способ создавать Пользовательские наложения в любом месте на карте, и таким образом мы можем получить полный контроль над маркерами карты.

API JavaScript Карт Google предоставляет класс OverlayView для создания ваших собственных наложений. OverlayView - это базовый класс, который предоставляет несколько методов, которые необходимо реализовать при создании наложений. Класс также предоставляет несколько методов, которые позволяют переводить координаты экрана и местоположения на карте.

Как мы уже говорили выше, наша практика при разработке компонентов карты заключается в том, чтобы начинать с API:

<uc-map-with-clusters source-function="$ctrl.getMapData()">
  <!-- Map clusters are repeated for each item from the source data -->
  <uc-map-cluster item="item">
    <uc-map-cluster-hover>{{item.title}}</uc-map-cluster-hover>
    <uc-map-cluster-active>{{item.title}} Active state</uc-map-cluster-active>
  </uc-map-cluster>
</uc-map-with-clusters>

На основе этого API у нас есть uc-map-with-clusterters, который создает элемент карты, а затем создает каждый экземпляр uc-map-cluster, который будет настраиваемым OverlayView. В Angular этот API требует использования $ compile для привязки данных к этим элементам DOM.

Самая важная часть этого API заключается в том, что нам нужно использовать директиву для отсоединения и повторения HTML, аналогично ng-repeat, но повторяя, как выберет наше приложение. Каждый маркер будет создан путем вызова $ compile для HTML и необходимой области.

export default function clustersDirective() {
  return {
    controller,
    scope: true,
    bindToController: {
      // App-maker must configure a data loader callback
      sourceFunction: '&',
    },
    compile(tElement) {
      // Templates are detached at compile time, then cleared.
      // They are rendered dynamically. Cloning them for IE-11; otherwise
      // template child elements get lost.
      const clusterTemplate = tElement.find('uc-map-cluster').clone();
      tElement.html('');
return {
        post(scope, element, attrs, [clustersCtrl]) {
          clustersCtrl.template = clusterTemplate;
        },
      };
    },
  };
}

Собрав все части вместе, мы получим пример JSFiddle с маркерами карты, скомпилированными с помощью angular:

Https://jsfiddle.net/wylieconlon/7Lsp5ckz/

Вы хотите работать над интересными картографическими приложениями?

Обратитесь в Компас для работы над нашей технологией в сфере недвижимости!