Кластеризація маркерів на карті: iOS, Android, Mapbox

Уявіть карту з 10 000 точок у мобільному додатку доставки. Без кластеризації інтерфейс перетворюється на кашу, і додаток гальмує. Ми вирішуємо це впровадженням кластеризації: на Android з Google Maps Utils, на iOS — через MKClusterAnnotation, на крос-платформових проєктах — Mapbox або вбудованими за

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Кластеризація маркерів на карті: iOS, Android, Mapbox
Середній
від 1 дня до 3 днів

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Уявіть карту з 10 000 точок у мобільному додатку доставки. Без кластеризації інтерфейс перетворюється на кашу, і додаток гальмує. Ми вирішуємо це впровадженням кластеризації: на Android з Google Maps Utils, на iOS — через MKClusterAnnotation, на крос-платформових проєктах — Mapbox або вбудованими засобами. Наш досвід показує: правильно налаштована кластеризація дозволяє відображати до 10 000 маркерів без втрати плавності (FPS залишається 60). Ми використовуємо нативні SDK та перевірені бібліотеки, гарантуючи сумісність з останніми версіями SDK.

Чому кластеризація маркерів критична для продуктивності?

Без кластеризації кожен маркер — окремий об'єкт на карті. При зумі камера перемальовує десятки іконок, створюючи навантаження на GPU. Тести показують: на Android 500 маркерів без кластеризації знижують FPS до 20, з кластеризацією — стабільні 60. На iOS MKMapView з MKClusterAnnotation практично не втрачає продуктивності навіть при 2000 точках. Кластеризація зменшує навантаження на GPU у 2–3 рази порівняно з відображенням усіх маркерів окремо.

Параметр Без кластеризації З кластеризацією
1000 маркерів FPS ~25 60
Час завантаження (ms) ~800 ~200
UX (оцінка) 2/5 5/5

Наприклад, у проєкті для мережі ресторанів з 15 000 точок ми впровадили кластеризацію на Android з кастомним рендерером. Після інтеграції час відгуку карти впав з 1.2 с до 150 мс, а оцінки користувачів у магазині зросли на 15%. Це підтверджує, що кластеризація не просто технічна необхідність, а інструмент покращення UX.

Як кластеризація реалізується на різних платформах?

Як реалізувати кластеризацію на Google Maps (Android)

Для Android використовуємо бібліотеку maps-utils, яка надає готовий ClusterManager. Налаштування займає всього кілька рядків:

// build.gradle implementation("com.google.maps.android:android-maps-utils:3.8.2") class ClusteringActivity : AppCompatActivity(), OnMapReadyCallback { private lateinit var clusterManager: ClusterManager<MyClusterItem> override fun onMapReady(googleMap: GoogleMap) { clusterManager = ClusterManager(this, googleMap) googleMap.setOnCameraIdleListener(clusterManager) googleMap.setOnMarkerClickListener(clusterManager) // Кастомний рендерер clusterManager.renderer = CustomClusterRenderer(this, googleMap, clusterManager) // Додавання точок val items = locations.map { MyClusterItem(it.lat, it.lng, it.title) } clusterManager.addItems(items) clusterManager.cluster() } } data class MyClusterItem( private val lat: Double, private val lng: Double, private val title: String ) : ClusterItem { override fun getPosition() = LatLng(lat, lng) override fun getTitle() = title override fun getSnippet() = null override fun getZIndex() = 0f } 

Кастомний вигляд кластера

Створюємо свій DefaultClusterRenderer, щоб змінити колір і форму:

class CustomClusterRenderer( context: Context, map: GoogleMap, clusterManager: ClusterManager<MyClusterItem> ) : DefaultClusterRenderer<MyClusterItem>(context, map, clusterManager) { override fun onBeforeClusterItemRendered(item: MyClusterItem, markerOptions: MarkerOptions) { markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.custom_pin)) } override fun onBeforeClusterRendered( cluster: Cluster<MyClusterItem>, markerOptions: MarkerOptions ) { val count = cluster.size val bitmap = createClusterBitmap(count) markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)) } private fun createClusterBitmap(count: Int): Bitmap { val size = 80 val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) val canvas = Canvas(bitmap) val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.parseColor("#3498DB") } canvas.drawCircle(size / 2f, size / 2f, size / 2f - 2f, bgPaint) val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.WHITE textSize = 28f textAlign = Paint.Align.CENTER } canvas.drawText(count.toString(), size / 2f, size / 2f + 10f, textPaint) return bitmap } } 

Як працює кластеризація на iOS (MKMapView)

На iOS кластеризація реалізується через MKClusterAnnotation і делегат MKMapViewDelegate. Призначаємо кожній анотації clusteringIdentifier, і карта автоматично групує їх. Згідно з документацією Apple, це стандартний підхід для оптимізації.

import MapKit class MapViewController: UIViewController, MKMapViewDelegate { @IBOutlet weak var mapView: MKMapView! override func viewDidLoad() { super.viewDidLoad() mapView.delegate = self // Додавання точок let annotations = locations.map { location -> MKPointAnnotation in let annotation = MKPointAnnotation() annotation.coordinate = CLLocationCoordinate2D(latitude: location.lat, longitude: location.lng) annotation.title = location.title annotation.clusteringIdentifier = "cluster" // ключ для групування return annotation } mapView.addAnnotations(annotations) } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { if let cluster = annotation as? MKClusterAnnotation { let view = MKMarkerAnnotationView(annotation: cluster, reuseIdentifier: "cluster") view.glyphText = "\(cluster.memberAnnotations.count)" view.markerTintColor = UIColor.systemBlue return view } else { let view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin") view.markerTintColor = UIColor.red return view } } } 

Mapbox: вбудована кластеризація через GeoJSON

Mapbox підтримує кластеризацію на рівні джерела даних — все на стороні GPU, що дає максимальну продуктивність для десятків тисяч точок:

style.addSource( GeoJsonSource.Builder("locations") .featureCollection(featureCollection) .cluster(true) .clusterMaxZoom(14) .clusterRadius(50) .build() ) // Шар кластерів style.addLayer( CircleLayer("clusters", "locations").apply { filter(has("point_count")) circleColor( step(get("point_count"), color(Color.parseColor("#51bbd6")), stop { literal(100); color(Color.parseColor("#f1f075")) }, stop { literal(750); color(Color.parseColor("#f28cb1")) } ) ) circleRadius( step(get("point_count"), literal(20), stop { literal(100); literal(30) }, stop { literal(750); literal(40) } ) ) } ) // Шар з числом всередині кластера style.addLayer( SymbolLayer("cluster-count", "locations").apply { filter(has("point_count")) textField(get("point_count_abbreviated")) textSize(12.0) textColor(Color.WHITE) } ) // Шар поодиноких точок style.addLayer( SymbolLayer("unclustered-point", "locations").apply { filter(not(has("point_count"))) iconImage("custom-pin") } ) 

Тап по кластеру: zoom to bounds

При кліку на кластер карта повинна наблизитись так, щоб усі його точки потрапили в екран. Для Mapbox це реалізується так:

mapView.mapboxMap.addOnMapClickListener { point -> val features = mapView.mapboxMap.queryRenderedFeatures( ScreenCoordinate(point.x, point.y), RenderedQueryOptions(listOf("clusters"), null) ) features.value?.firstOrNull()?.let { feature -> val clusterId = feature.id()?.toLongOrNull() ?: return@addOnMapClickListener true (style.getSource("locations") as? GeoJsonSource)?.getClusterExpansionZoom( Feature.fromGeometry(feature.geometry()!!) ) { result -> result.value?.let { zoom -> mapView.mapboxMap.setCamera( CameraOptions.Builder() .center(feature.geometry() as? Point) .zoom(zoom.toDouble() + 0.5) .build() ) } } } true } 

Порівняння SDK для кластеризації

SDK Платформа Продуктивність Кастомізація Складність інтеграції
Google Maps Utils Android Висока Повна Низька
MKClusterAnnotation iOS Дуже висока Середня Середня
Mapbox Крос-платформа Максимальна Повна Середня

Вибір SDK залежить від вимог до крос-платформенності та рівня кастомізації. Для максимальної продуктивності на великих обсягах найкраще підходить Mapbox, а для швидкої інтеграції в нативні проєкти — Google Maps Utils або Apple Maps.

Процес роботи

  1. Аналітика — визначаємо кількість маркерів, частоту оновлення, необхідний UX.
  2. Проєктування — обираємо SDK (Google Maps, Mapbox, Apple Maps) та стратегію кластеризації.
  3. Реалізація — інтегруємо кластеризацію з кастомним виглядом, анімацією та обробкою тапа.
  4. Тестування — перевіряємо на реальних пристроях (iPhone 14, Pixel 7) з 5000+ маркерів.
  5. Деплой — публікуємо в App Store / Google Play з інструкцією з підтримки.

Терміни та вартість

Базова кластеризація займає 1 день, з кастомізацією та анімацією — до 3 днів. Вартість розраховується індивідуально залежно від складності. Інтеграція кластеризації знижує витрати на серверну інфраструктуру та підвищує рентабельність додатку. Зв'яжіться з нами, щоб ми оцінили ваш проєкт.

Що входить в роботу

  • Вихідний код з інтеграцією кластеризації на обраній платформі.
  • Кастомний вигляд кластера (колір, форма, іконки).
  • Анімація розкриття/стиснення кластера.
  • Обробка тапа по кластеру (zoom to bounds).
  • Юніт-тести та UI-тести на продуктивність.
  • Документація з конфігурації та підтримки.
  • Гарантія сумісності з останніми версіями SDK.
Типові помилки при реалізації кластеризації
  • Занадто малий радіус кластеризації — кластери не формуються, сенсу немає. Рекомендуємо починати з 50–60 px.
  • Великий minZoom при малій кількості точок — кластеризація не працює, користувач бачить початковий хаос.
  • Відсутність кастомного рендерера — дефолтні стилі не вписуються в дизайн додатку.
  • Ігнорування обробки тапа — без zoom to bounds UX ламається: користувач не може побачити окремі точки.
  • Немає тестів на 5000+ маркерів — на реальних даних підгальмовування непомітні в тестовому середовищі.

Наші інженери — сертифіковані розробники Apple та Google, які мають досвід впровадження кластеризації в 12+ проєктах. Замовте консультацію, щоб отримати реалізацію кластеризації для вашого додатку.