Утилита для кластеризации маркеров Google Карт на Android

Кластеризация маркеров позволяет добавить на карту большое количество маркеров без ухудшения ее удобочитаемости.

Введение

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

Утилита для кластеризации маркеров позволяет управлять несколькими маркерами на разных уровнях масштабирования. При этом маркеры фактически обрабатываются как объекты, а отрисовываются как маркеры. Однако для ясности в этой статье всегда используется термин "маркеры".

Когда пользователь просматривает карту в большом масштабе, на ней отображаются отдельные маркеры. При уменьшении масштаба маркеры собираются в кластеры, что упрощает чтение карты. Утилита для кластеризации маркеров входит в состав библиотеки утилит Maps SDK для Android. Если вы ещё не настроили эту библиотеку, выполните рекомендации из руководства по настройке, прежде чем продолжить чтение этой страницы.

Карта с кластеризованными маркерами
Кластеризованные маркеры

Чтобы воспользоваться утилитой кластеризации маркеров, необходимо добавить маркеры в виде объектов ClusterItem в ClusterManager. ClusterManager передает маркеры в Algorithm, который преобразует их в набор кластеров. ClusterRenderer отвечает за прорисовку, добавляя и удаляя кластеры и отдельные маркеры. Классы ClusterRenderer и Algorithm являются подключаемыми модулями и их можно настраивать.

В комплект библиотеки утилит входит демонстрационное приложение, в котором приведены примеры реализации утилиты для кластеризации маркеров. О том, как запустить демонстрационное приложение, рассказывается в руководстве по настройке. Демонстрационное приложение включает в себя следующие примеры кластеризации маркеров:

  • ClusteringDemoActivity – простая операция, демонстрирующая кластеризацию маркеров.
  • BigClusteringDemoActivity – кластеризация 2000 маркеров.
  • CustomMarkerClusteringDemoActivity – создание собственного макета для кластеризованных маркеров.

Как добавить простое средство кластеризации маркеров

Чтобы создать простой кластер, состоящий из 10 маркеров, выполните описанные ниже действия. Результат будет иметь следующий вид, хотя количество отображаемых и кластеризованных маркеров будет изменяться в зависимости от уровня масштабирования:

Карта с 10 кластеризованными маркерами
10 кластеризованных маркеров

Выполните следующие действия:

  1. Реализуйте класс ClusterItem для представления маркеров на карте. Объект кластера возвращает позицию маркера в виде объекта LatLng. Он также может возвращать название или фрагмент (необязательно).
  2. Добавьте новый объект ClusterManager, группирующий объекты кластеров (маркеры) с учетом уровня масштабирования.
  3. Свяжите метод OnCameraIdleListener() карты с объектом ClusterManager. Это нужно сделать, потому что в ClusterManager реализован прослушиватель.
  4. Если вы хотите добавить функции, срабатывающие при клике по маркеру, нужно связать метод OnMarkerClickListener() карты с объектом ClusterManager, поскольку в ClusterManager реализован прослушиватель.
  5. Передайте маркеры объекту ClusterManager.

Подробные инструкции. Чтобы создать простой кластер из десяти маркеров, сначала создайте класс MyItem, реализующий ClusterItem.

Java

public class MyItem implements ClusterItem {
    private final LatLng position;
    private final String title;
    private final String snippet;

    public MyItem(double lat, double lng, String title, String snippet) {
        position = new LatLng(lat, lng);
        this.title = title;
        this.snippet = snippet;
    }

    @Override
    public LatLng getPosition() {
        return position;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public String getSnippet() {
        return snippet;
    }
}
      

Kotlin

inner class MyItem(
    lat: Double,
    lng: Double,
    title: String,
    snippet: String
) : ClusterItem {

    private val position: LatLng
    private val title: String
    private val snippet: String

    override fun getPosition(): LatLng {
        return position
    }

    override fun getTitle(): String? {
        return title
    }

    override fun getSnippet(): String? {
        return snippet
    }

    init {
        position = LatLng(lat, lng)
        this.title = title
        this.snippet = snippet
    }
}
      

В операции, работающей с картой, добавьте ClusterManager и передайте ему объекты кластеров. Обратите внимание на аргумент типа <MyItem>, декларирующий ClusterManager как относящийся к типу MyItem.

Java

// Declare a variable for the cluster manager.
private ClusterManager<MyItem> clusterManager;

private void setUpClusterer() {
    // Position the map.
    map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));

    // Initialize the manager with the context and the map.
    // (Activity extends context, so we can pass 'this' in the constructor.)
    clusterManager = new ClusterManager<MyItem>(context, map);

    // Point the map's listeners at the listeners implemented by the cluster
    // manager.
    map.setOnCameraIdleListener(clusterManager);
    map.setOnMarkerClickListener(clusterManager);

    // Add cluster items (markers) to the cluster manager.
    addItems();
}

private void addItems() {

    // Set some lat/lng coordinates to start with.
    double lat = 51.5145160;
    double lng = -0.1270060;

    // Add ten cluster items in close proximity, for purposes of this example.
    for (int i = 0; i < 10; i++) {
        double offset = i / 60d;
        lat = lat + offset;
        lng = lng + offset;
        MyItem offsetItem = new MyItem(lat, lng, "Title " + i, "Snippet " + i);
        clusterManager.addItem(offsetItem);
    }
}
      

Kotlin

// Declare a variable for the cluster manager.
private lateinit var clusterManager: ClusterManager<MyItem>

private fun setUpClusterer() {
    // Position the map.
    map.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.503186, -0.126446), 10f))

    // Initialize the manager with the context and the map.
    // (Activity extends context, so we can pass 'this' in the constructor.)
    clusterManager = ClusterManager(context, map)

    // Point the map's listeners at the listeners implemented by the cluster
    // manager.
    map.setOnCameraIdleListener(clusterManager)
    map.setOnMarkerClickListener(clusterManager)

    // Add cluster items (markers) to the cluster manager.
    addItems()
}

private fun addItems() {

    // Set some lat/lng coordinates to start with.
    var lat = 51.5145160
    var lng = -0.1270060

    // Add ten cluster items in close proximity, for purposes of this example.
    for (i in 0..9) {
        val offset = i / 60.0
        lat += offset
        lng += offset
        val offsetItem =
            MyItem(lat, lng, "Title $i", "Snippet $i")
        clusterManager.addItem(offsetItem)
    }
}
      

Вы также можете отключить анимацию кластеров при увеличении и уменьшении масштаба. Если анимация отключена, маркеры мгновенно перемещаются на нужные позиции, а не передвигаются в кластеры или из кластеров. Чтобы отключить анимацию, используйте метод setAnimation() в ClusterManager, как показано здесь:

Java

clusterManager.setAnimation(false);
      

Kotlin

clusterManager.setAnimation(false)
      

Как добавить информационное окно для отдельного кластеризованного маркера

Чтобы добавить информационное окно для конкретных кластеризованных маркеров, добавьте строки title (название) и snippet (фрагмент) в конструктор вашего класса ClusterItem.

В следующем примере показано, как реализовать в методе addItems() добавление маркера с информационным окном, задав значения полей title и snippet:

Java

// Set the lat/long coordinates for the marker.
double lat = 51.5009;
double lng = -0.122;

// Set the title and snippet strings.
String title = "This is the title";
String snippet = "and this is the snippet.";

// Create a cluster item for the marker and set the title and snippet using the constructor.
MyItem infoWindowItem = new MyItem(lat, lng, title, snippet);

// Add the cluster item (marker) to the cluster manager.
clusterManager.addItem(infoWindowItem);
      

Kotlin

// Set the lat/long coordinates for the marker.
val lat = 51.5009
val lng = -0.122

// Set the title and snippet strings.
val title = "This is the title"
val snippet = "and this is the snippet."

// Create a cluster item for the marker and set the title and snippet using the constructor.
val infoWindowItem = MyItem(lat, lng, title, snippet)

// Add the cluster item (marker) to the cluster manager.
clusterManager.addItem(infoWindowItem)
      

Как настроить кластеры маркеров

Конструктор класса ClusterManager создает объекты DefaultClusterRenderer и NonHierarchicalDistanceBasedAlgorithm. Вы можете изменить ClusterRenderer и Algorithm с помощью методов setAlgorithm(Algorithm<T> algorithm) и setRenderer(ClusterRenderer<T> view) класса ClusterManager.

Вы можете реализовать ClusterRenderer, чтобы изменить настройки отображения кластеров. В качестве исходного макета рекомендуется использовать DefaultClusterRenderer. Создав подкласс на основе DefaultClusterRenderer, вы можете изменить настройки по умолчанию.

Подробный пример настройки представлен в операции CustomMarkerClusteringDemoActivityдемонстрационного приложения из библиотеки утилит.

Карта с настраиваемой кластеризацией маркеров
Маркеры с настраиваемой кластеризацией

В CustomMarkerClusteringDemoActivity определяется объект кластера Person и реализуется его отрисовка за счет наследования класса DefaultClusterRenderer как PersonRenderer.

В демонстрационном приложении также показано, как реализовать интерфейс ClusterManager.OnClusterClickListener<Person> для отображения дополнительной информации о человеке (элементе Person) при клике по кластеру. Вы также можете реализовать ClusterManager.OnClusterItemClickListener<Person> аналогичным образом.

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