Графіки та візуалізація IoT-даних (температура, вологість, тиск)
Показати лінію температури за тиждень просто. Проблеми починаються коли даних 50 000 точок, користувач зумує до діапазону в 10 хвилин, і при цьому граф повинен прокручуватися плавно на бюджетному Android-пристрої. Неправильний вибір бібліотеки або наївна реалізація дає 3–4 FPS на скролл та OOM при спробі рендерити все разом.
Вибір бібліотеки для графіків
На Android — три реальних варіанти:
| Бібліотека | Продуктивність | Кастомізація | Особливості |
|---|---|---|---|
MPAndroidChart |
Хороша до ~5к точок | Середня | Mature, XML+Compose wrapper |
Vico |
Відмінна, Compose-first | Хороша | Нативний Compose, активна розробка |
Charts (Compose) |
Хороша | Базова | Прості кейси |
Для IoT з великим обсягом даних та зумом — Vico на Compose або MPAndroidChart з LineDataSet.setDrawCircles(false) + setMode(CUBIC_BEZIER) вимкнено (сплайн = дорого на великих наборах).
На iOS — Charts (fork MPAndroidChart для Swift) або DGCharts. Нативний Swift Charts (iOS 16+) — простий API, хорошая продуктивність, але обмежена кастомізація.
Downsampling: Ключ до продуктивності
50 000 точок за місяць — на екрані шириною 400dp поміститься максимум 400 точок. Відображати всі 50 000 — пуста робота GPU.
LTTB (Largest-Triangle-Three-Buckets) — алгоритм прореживання даних зі збереженням візуального профілю. На Android:
fun lttbDownsample(data: List<DataPoint>, threshold: Int): List<DataPoint> {
if (data.size <= threshold) return data
val sampled = mutableListOf<DataPoint>()
sampled.add(data.first())
val bucketSize = (data.size - 2).toDouble() / (threshold - 2)
var a = 0
for (i in 0 until threshold - 2) {
val bucketStart = ((i + 1) * bucketSize).toInt() + 1
val bucketEnd = minOf(((i + 2) * bucketSize).toInt() + 1, data.size - 1)
val nextA = bucketStart until bucketEnd
val avgX = nextA.sumOf { data[it].x } / nextA.count()
val avgY = nextA.sumOf { data[it].y } / nextA.count()
var maxArea = -1.0
var maxPoint = bucketStart
for (j in bucketStart until bucketEnd) {
val area = Math.abs(
(data[a].x - avgX) * (data[j].y - data[a].y) -
(data[a].x - data[j].x) * (avgY - data[a].y)
) * 0.5
if (area > maxArea) { maxArea = area; maxPoint = j }
}
sampled.add(data[maxPoint])
a = maxPoint
}
sampled.add(data.last())
return sampled
}
При зуме до короткого діапазону — завантажувати исходные дані з сервера для цього періоду без downsampling. Ширина діапазону < 1 часа → запит даних з оригінальним дозволом.
Зум та скролл
Жест pinch-to-zoom для графіка — через ScaleGestureDetector (Android) або MagnificationGesture / onMagnification modifier. При зміні діапазону — запит нових даних з сервера.
Паттерн «нескінченний скролл» для часових серій: при скролі до краю завантажених даних — подгрузити наступний період. Paging 3 для цього використовувати не можна напрямку (він не для часових серій), але принцип той же: prefetchDistance — завантажувати дані коли до краю залишилось N одиниць часу.
Кілька параметрів на одному графіку
Температура + вологість на одній осі — погано: різні одиниці та діапазони. Правильно — дві осі Y (MPAndroidChart підтримує через axisLeft / axisRight) або два окремих синхронізованих графіки з спільною віссю X.
При синхронізованому скролі двох графіків — додайте OnChartGestureListener до кожного та при скролі одного програмно скроліть другий:
chart1.onChartGestureListener = object : OnChartGestureListener {
override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) {
chart2.viewPortHandler.setTranslation(chart1.viewPortHandler.transX, 0f)
chart2.invalidate()
}
// інші методи інтерфейсу...
}
Аннотації та подій на графіку
Точки аномалій, пороговые лінії, подій (відкриття двері, перезавантаження пристрою) — важливі для аналізу. LimitLine у MPAndroidChart для горизонтальних порогів. Точкові аннотації — користувацький MarkerView або VerticalHighlight з іконкою.
Кольорові діапазони
Для температури: зелений (норма) → жовтий (попередження) → червоний (критично). LinearGradient по Y-осі у Compose Canvas або GradientColor у MPAndroidChart. Візуально одразу зрозуміло без легенди де проблемний період.
Реалізація графіків IoT-даних з зумом, downsampling та аннотаціями: 3–5 тижнів. Стоимость зависит від числа параметрів, источников данных и требований к кастомизации.







