Interactive IoT Data Charts Implementation
We often see: displaying a temperature line over a week is trivial. But with 500,000 records per month, data arriving at 1 Hz, the user zooms to a 10-minute range and expects smooth scrolling on a budget Android device – that requires expertise. Our team has implemented dozens of such solutions over years. A wrong library choice or naive implementation yields 3–4 FPS on scroll and OOM when trying to render everything at once. We guarantee stable performance under load. The typical mistake is rendering all points; thinning algorithms save performance.
Why IoT Data Visualization Requires Downsampling
50,000 points for a month – on a 400dp wide screen you can fit at most 400 points. Rendering all 50,000 is wasted GPU work. LTTB (Largest-Triangle-Three-Buckets) is a data thinning algorithm that preserves the visual profile. It is 10 times faster than rendering all points. On 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 } When zooming to a short range – fetch raw data from server for that period without downsampling. Range width < 1 hour → request data with original resolution.
This approach is described in Svegling (2014).
How to Choose a Chart Library
On Android – three real options:
| Library | Performance | Customization | Notes |
|---|---|---|---|
MPAndroidChart |
Good up to ~5k points | Medium | Mature, XML+Compose wrapper |
Vico |
Excellent, Compose-first | Good | Native Compose, active development |
Charts (Compose) |
Good | Basic | Simple cases |
For IoT with large data volume and zoom – Vico on Compose or MPAndroidChart with LineDataSet.setDrawCircles(false) + setMode(CUBIC_BEZIER) disabled (spline is expensive on large sets).
On iOS – Charts (fork of MPAndroidChart for Swift) or DGCharts. Native Swift Charts (iOS 16+) – simple API, good performance, but limited customization.
Zoom and Scroll
Pinch-to-zoom gesture on chart – via ScaleGestureDetector (Android) or MagnificationGesture / onMagnification modifier. When range changes – request new data from server.
Infinite scroll pattern for time series: when scrolling to edge of loaded data – load next period. Paging 3 cannot be used directly (not designed for time series), but same principle: prefetchDistance – load data when N time units left to edge.
Multiple Parameters on One Chart
Temperature + humidity on same axis – bad: different units and ranges. Correct – two Y-axes (MPAndroidChart supports axisLeft / axisRight) or two separate synchronized charts sharing X-axis.
For synchronized scroll of two charts – add OnChartGestureListener to each and programmatically scroll the second when first scrolls:
chart1.onChartGestureListener = object : OnChartGestureListener { override fun onChartTranslate(me: MotionEvent?, dX: Float, dY: Float) { chart2.viewPortHandler.setTranslation(chart1.viewPortHandler.transX, 0f) chart2.invalidate() } // other interface methods... } Annotations and Events on Charts
Anomaly points, threshold lines, events (door open, device restart) – important for analysis. LimitLine in MPAndroidChart for horizontal thresholds. Point annotations – custom MarkerView or VerticalHighlight with icon.
Color Ranges
For temperature: green (normal) → yellow (warning) → red (critical). LinearGradient along Y-axis in Compose Canvas or GradientColor in MPAndroidChart. Visually clear without legend where problem period occurs.
What Is Included in Our Chart Implementation Work
| Stage | Result |
|---|---|
| Requirements analysis | Technical specification with prototype |
| Library and architecture selection | Documented rationale |
| Downsampling implementation | Optimized module for Android/iOS |
| Integration with data sources | REST/GraphQL adapter for telemetry |
| Testing on real devices | Performance report (FPS, RAM) |
| Optimization for iOS and Android | Smooth performance on older devices |
| Code and documentation delivery | Source code, deployment instructions, 3-month support |
Implementation details: synchronizing multiple charts
When synchronizing scroll of two charts with a common X-axis, `OnChartGestureListener` is used for each. When one chart scrolls, the translation of the second is programmatically changed. This ensures a unified time scale for different parameters.Our Experience and Guarantees
We have been in mobile development for years and have delivered 30+ projects in the IoT field. We guarantee stable chart performance under loads of up to 100,000 points. We provide full documentation and training for the client's developers.
Order the implementation of charts for your IoT app. Contact us to discuss your requirements. We will evaluate the task within one business day and offer a turnkey solution. Get expert consultation on library and architecture selection, tailored to your devices and scenarios.
IoT data chart implementation with zoom, downsampling, and annotations: from 3 to 5 weeks per client. Cost is calculated individually based on the number of parameters, data sources, and customization needs.







