All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tri.covid19.coda.hotspot.HotspotTable.kt Maven / Gradle / Ivy

/*-
 * #%L
 * coda-app
 * --
 * Copyright (C) 2020 - 2022 Elisha Peterson
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package tri.covid19.coda.hotspot

import javafx.beans.property.*
import javafx.event.EventTarget
import javafx.scene.control.SplitPane
import javafx.scene.control.TableView
import tornadofx.*
import tri.area.usa.UsaAreaLookup
import tri.covid19.DEATHS
import tri.covid19.coda.history.*
import tri.covid19.coda.utils.*
import tri.covid19.coda.data.CovidTimeSeriesSources

/** UI for exploring historical COVID time series data. */
class HotspotTable: SplitPane() {

    val selectedMetric = SimpleStringProperty(DEATHS).apply { addListener { _ -> updateTableData() } }
    val hotspotData = mutableListOf().asObservable()
    val minPopulation = SimpleObjectProperty(0).apply { addListener { _ -> updateTableData() } }
    val maxPopulation = SimpleObjectProperty(Int.MAX_VALUE).apply { addListener { _ -> updateTableData() } }
    val minCount = SimpleObjectProperty(100).apply { addListener { _ -> updateTableData() } }
    val minPerCapitaCount = SimpleObjectProperty(0).apply { addListener { _ -> updateTableData() } }
    val minLastWeekCount = SimpleObjectProperty(100).apply { addListener { _ -> updateTableData() } }
    val minLastWeekPerCapitaCount = SimpleObjectProperty(20).apply { addListener { _ -> updateTableData() } }

    val regionTypes = listOf(COUNTRIES, STATES, COUNTIES, CBSA)
    val selectedRegionType = SimpleStringProperty(regionTypes[1]).apply { addListener { _ -> updateTableData() } }
    val parentRegion = SimpleStringProperty("USA").apply { addListener { _ -> updateTableData() } }

    lateinit var table: TableView

    init {
        configPanel()
        borderpane {
            top = toolbar {
                button("Copy") { action { copyTableDataToClipboard(table) } }
            }
            center = table()
        }
        updateTableData()
    }

    /** Configuration for the table. */
    private fun EventTarget.configPanel() = form {
        fieldset("Region/Metric") {
            field("Category") {
                combobox(selectedRegionType, regionTypes)
            }
            field("Metric") {
                combobox(selectedMetric, METRIC_OPTIONS)
            }
        }
        fieldset("Filtering") {
            field("Parent Region") {
                textfield(parentRegion)
            }
            field("Min Population") {
                editablespinner(0..Int.MAX_VALUE).bind(minPopulation)
            }
            field("Max Population") {
                editablespinner(0..Int.MAX_VALUE).bind(maxPopulation)
            }
            field("Min Total") {
                editablespinner(0..100000000).bind(minCount)
            }
            field("Min (per capita)") {
                editablespinner(0..10000).bind(minPerCapitaCount)
            }
            field("Min (Last 7 Days)") {
                editablespinner(0..10000).bind(minLastWeekCount)
            }
            field("Min (per capita, Last 7 Days)") {
                editablespinner(0..10000).bind(minLastWeekPerCapitaCount)
            }
        }
    }

    /** Table. */
    private fun EventTarget.table() = scrollpane(fitToWidth = true, fitToHeight = true) {
        tableview(hotspotData) {
            readonlyColumn("Region", HotspotInfo::regionId)
            readonlyColumn("FIPS", HotspotInfo::fips)
            readonlyColumn("Population", HotspotInfo::population).cellFormatUserNumber()
            readonlyColumn("Metric", HotspotInfo::metric)

            readonlyColumn("Total", HotspotInfo::value).cellFormatUserNumber()
            readonlyColumn("(per 100k)", HotspotInfo::valuePerCapita).cellFormatUserNumber()
            readonlyColumn("Last 28", HotspotInfo::dailyChange28).cellFormatUserNumber()
            readonlyColumn("(per 100k)", HotspotInfo::dailyChange28PerCapita).cellFormatUserNumber()
            readonlyColumn("Last 7", HotspotInfo::dailyChange7).cellFormatUserNumber()
            readonlyColumn("(per 100k)", HotspotInfo::dailyChange7PerCapita).cellFormatUserNumber()
            readonlyColumn("(7days/total)", HotspotInfo::percentInLast7).cellFormatPercentage()
            readonlyColumn("(7days/28days)", HotspotInfo::percentInLast7Of28).cellFormatPercentage()

            readonlyColumn("3Day %Δ", HotspotInfo::threeDayPercentChange).cellFormatPercentage()
            readonlyColumn("7Day %Δ", HotspotInfo::sevenDayPercentChange).cellFormatPercentage()

            readonlyColumn("Trend", HotspotInfo::trendDays).cellFormatDayTrend()
            readonlyColumn("(since last extreme)", HotspotInfo::changeSinceTrendExtremum).cellFormatPercentage()

            readonlyColumn("Doubling Time", HotspotInfo::doublingTimeDays).cellFormatUserNumber()
            readonlyColumn("(last 28 days)", HotspotInfo::doublingTimeDays28).cellFormatUserNumber()
            readonlyColumn("(last 14 days)", HotspotInfo::doublingTimeDays14).cellFormatUserNumber()
            readonlyColumn("(ratio)", HotspotInfo::doublingTimeDaysRatio).cellFormatUserNumber()

            readonlyColumn("Peak 7-Day Total", HotspotInfo::peak7).cellFormatUserNumber()
            readonlyColumn("(per 100k)", HotspotInfo::peak7PerCapita).cellFormatUserNumber()
            readonlyColumn("(date)", HotspotInfo::peak7Date)
            readonlyColumn("Peak 14-Day Total", HotspotInfo::peak14).cellFormatUserNumber()
            readonlyColumn("(per 100k)", HotspotInfo::peak14PerCapita).cellFormatUserNumber()
            readonlyColumn("(date)", HotspotInfo::peak14Date)

            readonlyColumn("Severity (#)", HotspotInfo::severityByChange)
            readonlyColumn("Severity (rate)", HotspotInfo::severityByDoubling)
            readonlyColumn("Severity (total)", HotspotInfo::totalSeverity)
//            readonlyColumn("Trend", HotspotInfo::severityChange)
//            readonlyColumn("3/7% Ratio", HotspotInfo::threeSevenPercentRatio).cellFormatUserNumber()

            table = this
        }
    }

    private fun updateTableData() {
        hotspotData.setAll(data()
                .filter { parentRegion.value == null || UsaAreaLookup.area(it.areaId).parent == UsaAreaLookup.area(parentRegion.value) }
                .filter { it.lastValue >= minCount.value && it.deltas().last(0..6).sum() >= minLastWeekCount.value }
                .filter { UsaAreaLookup.area(it.areaId).population == null || it.lastValue / UsaAreaLookup.area(it.areaId).population!! * 1E5 >= minPerCapitaCount.value }
                .filter { UsaAreaLookup.area(it.areaId).population == null || it.deltas().last(0..6).sum() / UsaAreaLookup.area(it.areaId).population!! * 1E5 >= minLastWeekPerCapitaCount.value }
                .hotspotPerCapitaInfo(metric = selectedMetric.value, minPopulation = minPopulation.value, maxPopulation = maxPopulation.value))
    }

    internal fun data() = when (selectedRegionType.value) {
        COUNTRIES -> CovidTimeSeriesSources.countryData(includeGlobal = true)
        STATES -> CovidTimeSeriesSources.usStateData(includeUS = true)
        COUNTIES -> CovidTimeSeriesSources.usCountyData()
        CBSA -> CovidTimeSeriesSources.usCbsaData()
        else -> throw IllegalStateException()
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy