Ph0tonic/SDP_Projet

View on GitHub
app/src/main/java/ch/epfl/sdp/map/MapboxHeatmapPainter.kt

Summary

Maintainability
A
0 mins
Test Coverage
A
98%
package ch.epfl.sdp.map

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import ch.epfl.sdp.database.data.HeatmapData
import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
import com.mapbox.mapboxsdk.maps.Style
import com.mapbox.mapboxsdk.style.expressions.Expression
import com.mapbox.mapboxsdk.style.layers.CircleLayer
import com.mapbox.mapboxsdk.style.layers.PropertyFactory
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource
import java.util.*

class MapboxHeatmapPainter(val style: Style,
                           val heatmapData: MutableLiveData<HeatmapData>,
                           belowLayerId: String) : MapboxPainter {

    companion object {
        private val BLUE = Expression.rgb(0, 0, 255)
        private val CYAN = Expression.rgb(0, 255, 255)
        private val GREEN = Expression.rgb(0, 255, 0)
        private val ORANGE = Expression.rgb(255, 255, 0)
        private val RED = Expression.rgb(255, 0, 0)

        private fun unclusteredLayerData(heatmapId: String, style: Style, belowLayerId: String) {
            val unclustered = CircleLayer("unclustered-points$heatmapId", heatmapId)
            unclustered.setProperties(
                    PropertyFactory.circleColor(
                            Expression.interpolate(Expression.linear(), Expression.get("intensity"),
                                    Expression.stop(1, BLUE),
                                    Expression.stop(3, CYAN),
                                    Expression.stop(5, GREEN),
                                    Expression.stop(7, ORANGE),
                                    Expression.stop(9, RED)
                            )
                    ),
                    PropertyFactory.circleRadius(25f),
                    PropertyFactory.circleBlur(2f),
                    PropertyFactory.circleOpacity(0.7f)
            )
            unclustered.setFilter(Expression.neq(Expression.get("cluster"), Expression.literal(true)))
            style.addLayerBelow(unclustered, belowLayerId)
        }

        private fun clusteredLayerData(heatmapId: String, style: Style, belowLayerId: String) {
            val clustered = CircleLayer("clustered-points-$heatmapId", heatmapId)
            clustered.setProperties(
                    PropertyFactory.circleColor(RED),
                    PropertyFactory.circleRadius(40f),
                    PropertyFactory.circleBlur(1f)
            )
            clustered.setFilter(Expression.eq(Expression.get("cluster"), Expression.literal(true)))
            style.addLayerBelow(clustered, belowLayerId)
        }
    }

    private val uuidSource = UUID.randomUUID().toString()

    private val geoJsonSource: GeoJsonSource = GeoJsonSource(uuidSource, GeoJsonOptions()
            .withCluster(true)
            .withClusterProperty("intensities", Expression.literal("+"), Expression.get("intensity"))
            .withClusterMaxZoom(13)
    )
    private val heatmapRedrawObserver = Observer<HeatmapData> { paint() }

    init {
        unclusteredLayerData(uuidSource, style, belowLayerId)
        clusteredLayerData(uuidSource, style, belowLayerId)
        style.addSource(geoJsonSource)
        heatmapData.observeForever(heatmapRedrawObserver)

        // first call is not triggered by observer
        paint()
    }

    override fun onDestroy() {
        heatmapData.removeObserver(heatmapRedrawObserver)
        geoJsonSource.setGeoJson(FeatureCollection.fromFeatures(listOf()))
    }

    private fun paint() {
        val features = FeatureCollection.fromFeatures(
                heatmapData.value!!.dataPoints.map {
                    val feature = Feature.fromGeometry(Point.fromLngLat(it.position!!.longitude, it.position!!.latitude))
                    feature.addNumberProperty("intensity", it.intensity)
                    feature
                }.toMutableList()
        )
        geoJsonSource.setGeoJson(features)
    }
}