Реализация карты расположения IoT-устройств в мобильном приложении
Сто датчиков на карте. Половина — онлайн, треть — в офлайне уже три часа, несколько — с разрядившейся батарейкой. Пользователю нужно за секунду понять, где проблема, и нажать на конкретный датчик, чтобы увидеть последние показания. Это стандартная задача IoT-мониторинга — и она решается без экзотики.
Отображение устройств на карте
Маркер IoT-устройства несёт статус: online, offline, warning, critical. Цветовое кодирование — зелёный/серый/жёлтый/красный. Иконка меняется при обновлении через WebSocket без перезагрузки карты.
На Android Google Maps SDK: обновляем BitmapDescriptor маркера через marker.setIcon(getStatusIcon(device.status)). Чтобы не создавать новый Bitmap на каждое обновление статуса — кешируем иконки по четырём статусам в HashMap<Status, BitmapDescriptor> при старте.
На iOS MapKit: MKAnnotationView.image переназначаем через mapView(_:viewFor:) при изменении данных. Для плавного перехода цвета — UIView.transition(with:duration:options:animations:) на смену иконки.
В Flutter через Google Maps Flutter plugin: Marker(icon: BitmapDescriptor.fromBytes(pngBytes)). Важно: BitmapDescriptor.fromAssetImage вызывается асинхронно, если вызывать его при каждом обновлении маркера — будут джанки. Создаём все иконки заранее в initState и кешируем.
Кластеризация при большом количестве устройств
При 100+ устройствах в одном viewport кластеризация обязательна. Для Flutter: google_maps_cluster_manager package. Для Android нативно: com.google.maps.android:android-maps-utils с DefaultClusterRenderer. Для iOS: GMUClusterManager из Maps Utils SDK.
Кастомный рендер кластера: показываем не просто число, а статус «худшего» устройства в кластере — если хоть одно critical, кластер красный. Это позволяет диспетчеру мгновенно увидеть проблемные зоны без раскрытия каждого кластера.
Детальный экран устройства
Нажатие на маркер — bottom sheet или navigation push с последними данными устройства: координата, время последнего пакета, показания датчиков, заряд батареи, уровень сигнала. Данные запрашиваются в момент открытия через REST API /devices/{id}/latest, кешируются локально на 30 секунд.
История местоположения: если устройство мобильное (трекер, счётчик на транспорте) — трек за последние N часов. Если стационарное (датчик на здании) — только текущая позиция и факт онлайна.
Обновление в реальном времени
WebSocket-подписка на обновления флота. Важно не перерисовывать всю карту при каждом сообщении — обновляем только изменившиеся маркеры. Diff-подход: сравниваем device.updatedAt и обновляем только те объекты, где timestamp изменился.
При закрытии приложения WebSocket закрывается. При возврате — пересоздаётся. Обработка reconnect: экспоненциальная задержка (1 сек → 2 → 4 → 8 → max 30 сек).
Поиск и фильтрация устройств
Список устройств рядом с картой — фильтрация по статусу, типу, группе. На Flutter — ListView.builder с локальной фильтрацией по List<Device>, без дополнительных запросов к серверу. Поиск по имени устройства — TextEditingController с debounce 300 мс.
На Android — RecyclerView с DiffUtil.Callback для оптимального diff при обновлении. Быстрый скролл по 200+ устройств без лагов за счёт view recycling и @Stable-аннотаций на моделях (Compose).
Сортировка: по статусу (критичные вверху), по расстоянию от пользователя, по имени. Сортировка по расстоянию требует текущую геолокацию пользователя — запрашиваем один раз при открытии экрана, не обновляем постоянно.
Сроки
Карта устройств с кластеризацией, цветовыми статусами, детальным экраном и списком с фильтрацией при готовом API: 4 часа — 2 рабочих дня в зависимости от кастомизации маркеров и требований к UI. Стоимость рассчитывается после уточнения требований.







