Wednesday, 16 September 2020

MPChart won't display hourly weather data

I'm creating a Weather app and i'm trying to display an MPChart which will use the hourly temps as Y points & the Hour (in "HH" format) as the XAxisLabels/ X points.

I'm creating the LineData for the chart in my Fragment's viewModel and expose it to the Fragment via liveData. I also create the XAxisLabels for the chart on my Fragment. The LineData seem to contain the right entries (viewModel: [Entry, x: 0.0 y: 39.07, Entry, x: 1.0 y: 38.68, Entry, x: 2.0 y: 38.16, Entry, x: 3.0 y: 37.29, Entry, x: 4.0 y: 36.2, Entry, x: 5.0 y: 35.22,........]), but the chart isn't even displaying any of the data. What am I missing here?

Here's what is rendered:

enter image description here

Here's the code:

ViewModel.kt

class WeatherViewModel(
    private val forecastRepository: ForecastRepository,
    private val weatherUnitConverter: WeatherUnitConverter,
    context: Context
) : ViewModel() {

val hourlyWeatherEntries = forecastRepository.getHourlyWeather()

val hourlyChartData = hourlyWeatherEntries.switchMap { hourlyData ->
        liveData {
            Log.d("ViewModel hourly data", "$hourlyData")
            val task = viewModelScope.async(Dispatchers.Default) {
                createChartData(hourlyData)
            }
            emit(task.await())
        }
    }

    /**
     * Creates the line chart's data and returns them.
     * @return The line chart's data (x,y) value pairs
     */
    private fun createChartData(hourlyWeather: List<HourWeatherEntry>?): LineData {
        if (hourlyWeather == null) return LineData()

        Log.d("ViewModel class", hourlyWeather.toString())

        val unitSystemValue = preferences.getString(UNIT_SYSTEM_KEY, "si")!!
        val values = arrayListOf<Entry>()

        for (i in hourlyWeather.indices) { // init data points
            // format the temperature appropriately based on the unit system selected
            val hourTempFormatted = when (unitSystemValue) {
                UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> hourlyWeather[i].temperature
                UnitSystem.US.name.toLowerCase(Locale.ROOT) -> weatherUnitConverter.convertToFahrenheit(
                    hourlyWeather[i].temperature
                )
                else -> hourlyWeather[i].temperature
            }

            // Create the data point
            values.add(
                Entry(

                    i.toFloat(),
                    hourTempFormatted.toFloat(),
                    appContext.resources.getDrawable(
                        determineSummaryIcon(hourlyWeather[i].icon),
                        null
                    )
                )
            )
        }
        Log.d("MainFragment viewModel", "$values")
        // create a data set and customize it
        val lineDataSet = LineDataSet(values, "")

        val color = appContext.resources.getColor(R.color.black, null)
        val offset = MPPointF.getInstance()
        offset.y = -35f

        lineDataSet.apply {
            valueFormatter = YValueFormatter()
            setDrawValues(true)
            fillDrawable = appContext.resources.getDrawable(R.drawable.gradient_night_chart, null)
            setDrawFilled(true)
            setDrawIcons(true)
            setCircleColor(color)
            mode = LineDataSet.Mode.HORIZONTAL_BEZIER
            this.color = color // line color
            iconsOffset = offset
            lineWidth = 3f
            valueTextSize = 9f
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                valueTypeface = appContext.resources.getFont(R.font.work_sans_medium)
            }
        }

        // create a LineData object using our LineDataSet
        val data = LineData(lineDataSet)
        data.apply {
            setValueTextColor(R.color.colorPrimary)
            setValueTextSize(15f)
        }
        return data
    }
}

MainFragment.kt

class MainFragment : Fragment() {

    // Lazy inject the view model
    private val viewModel: WeatherViewModel by viewModel()
private lateinit var hourlyChart: LineChart

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setUpChart()
        lifecycleScope.launch {
            // Shows a lottie animation while the data is being loaded
            //scrollView.visibility = View.GONE
            //lottieAnimView.visibility = View.VISIBLE
            bindUIAsync().await()
            // Stops the animation and reveals the layout with the data loaded
            //scrollView.visibility = View.VISIBLE
            //lottieAnimView.visibility = View.GONE
        }
    }

@SuppressLint("SimpleDateFormat")
    private fun bindUIAsync() = lifecycleScope.async(Dispatchers.Main) {
     

        // fetch hourly chart line data
        val hourlyChartData = viewModel.hourlyChartData

        hourlyChartData.observe(viewLifecycleOwner, Observer { lineData ->
            if(lineData == null) {
                Log.d("HOURLY CHART DATA", "Line data = null")
                return@Observer
            }

            hourlyChart.data = lineData
            hourlyChart.invalidate()
        })

        // fetch hourly weather
        val hourlyWeather = viewModel.hourlyWeatherEntries//viewModel.hourlyWeatherEntries

        // Observe the hourly weather live data
        hourlyWeather.observe(viewLifecycleOwner, Observer { hourly ->
            if (hourly == null) return@Observer

            val xAxisLabels = arrayListOf<String>()
            val sdf = SimpleDateFormat("HH")
            for (i in hourly.indices) {
                val formattedLabel = sdf.format(Date(hourly[i].time * 1000))
                xAxisLabels.add(formattedLabel)
            }
            setChartAxisLabels(xAxisLabels)
            Log.d(TAG, "label count = ${hourlyChart.xAxis.labelCount.toString()}")
        })

      
        return@async true
    }

    private fun setChartAxisLabels(labels: ArrayList<String>) {
        // Populate the X axis with the hour labels
        hourlyChart.xAxis.valueFormatter = IndexAxisValueFormatter(labels)
    }

    /**
     * Sets up the chart with the appropriate
     * customizations.
     */
    private fun setUpChart() {
        hourlyChart.apply {
            description.isEnabled = false
            setNoDataText("Data is loading...")

            // enable touch gestures
            setTouchEnabled(true)
            dragDecelerationFrictionCoef = 0.9f

            // enable dragging
            isDragEnabled = true
            isHighlightPerDragEnabled = true
            setDrawGridBackground(false)
            axisRight.setDrawLabels(false)
            axisLeft.setDrawLabels(false)
            axisLeft.setDrawGridLines(false)

            // disable zoom functionality
            setScaleEnabled(false)
            setPinchZoom(false)
            isDoubleTapToZoomEnabled = false

            // disable the chart's legend
            legend.isEnabled = false

            // append extra offsets to the chart's auto-calculated ones
            setExtraOffsets(0f, 0f, 0f, 10f)

            data = LineData()
            data.isHighlightEnabled = false
            setVisibleXRangeMaximum(6f)
            setBackgroundColor(resources.getColor(R.color.bright_White, null))
        }

        // X Axis setup
        hourlyChart.xAxis.apply {
            position = XAxis.XAxisPosition.BOTTOM
            textSize = 14f
            setDrawLabels(true)
            setDrawAxisLine(false)
            setDrawGridLines(false)
            isEnabled = true
            granularity = 1f // one hour
            spaceMax = 0.2f // add padding start
            spaceMin = 0.2f // add padding end
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                typeface = resources.getFont(R.font.work_sans)
            }
            textColor = resources.getColor(R.color.black, null)
        }

        // Left Y axis setup
        hourlyChart.axisLeft.apply {
            setDrawLabels(true)
            setDrawGridLines(false)
            setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
            // temperature values range (higher than probable temps in order to scale down the chart)
            axisMinimum = 0f
            axisMaximum = when (getUnitSystemValue()) {
                UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> 50f
                UnitSystem.US.name.toLowerCase(Locale.ROOT) -> 150f
                else -> 50f
            }
        }

        // Right Y axis setup
        hourlyChart.axisRight.apply {
            setDrawGridLines(false)
            isEnabled = false
        }
    }
}


from MPChart won't display hourly weather data

No comments:

Post a Comment