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

tri.area.Lookup.kt Maven / Gradle / Ivy

/*-
 * #%L
 * coda-data
 * --
 * Copyright (C) 2020 - 2021 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.area

import tri.util.csvResource
import tri.util.fine
import tri.util.javaTrim
import tri.util.warning
import java.nio.charset.Charset

/**
 * Quick access to looking up areas/elements by region name. Maintains a cache so that only one lookup is performed per
 * request string.
 */
object Lookup {

    //region CACHE

    private val areaCache = mutableMapOf().apply {
        listOf("US", "USA", "United States", "United States of America").forEach { this[it] = USA }
        listOf("Earth", "World").forEach { this[it] = EARTH }
        // prepopulate with state abbreviation "OH" and full name "Ohio, US"
        //   -- do not use name without US suffix because e.g. Georgia is ambiguous
        Usa.states.forEach { (abbrev, state) ->
            this[abbrev] = state
            this["${state.fullName}, US"] = state
        }
        // prepopulate with FEMA/HHS regions
        Usa.femaRegions.forEach { (_, area) ->
            this[area.id] = area
        }
        // prepopulate with Census regions
        Usa.censusRegions.forEach { (_, area) ->
            this[area.id] = area
        }
        this["X"] = Usa.regionX
        this["Y"] = Usa.regionY
        // prepopulate with county FIPS and combined id, e.g. "Cook, Illinois, US"
        Usa.counties.forEach {
            this[it.key.toString()] = it.value
            this[it.value.id] = it.value
        }
        // prepopulate with CBSA ids and titles
        Usa.cbsas.forEach {
            this[it.key.toString()] = it.value
            this[it.value.id] = it.value
            this["${it.value.id}, US"] = it.value
        }
        // prepopulate with e.g. "Unassigned, Ohio, US"
        Usa.unassigned.forEach {
            this["Unassigned, ${Usa.statesByAbbreviation[it.key]}, US"] = it.value
        }
    }
    private val notFound = mutableMapOf()

    //endregion

    //region PRIMARY LOOKUP

    /** Add area to lookup cache. */
    fun addArea(area: AreaInfo) {
        if (area.id !in areaCache.keys)
            areaCache[area.id] = area
    }

    /**
     * Get object for area with given name. Logs an error and returns a generic "Unknown" area if not found.
     * @param lookupName name to lookup
     * @param assumeUsState if true, lookup will assume the area is part of the USA if not found or ambiguous
     */
    fun area(lookupName: String, assumeUsState: Boolean = false) = areaOrNull(lookupName, assumeUsState) ?: UNKNOWN

    /** Get object for area with given name. Logs an error and returns null if not found. */
    fun areaOrNull(lookupName: String, assumeUsState: Boolean = false): AreaInfo? {
        val name = aliases[lookupName.javaTrim()] ?: lookupName
        val altName = if (assumeUsState) "$name, US" else null
        areaCache[name]?.let { return it }
        areaCache[altName]?.let { return it }
        notFound[name]?.let { return null }

        val jhuArea = JhuAreaData.lookupCaseInsensitive(name) ?: JhuAreaData.lookupCaseInsensitive(altName ?: "")
        val areaInfo = if (jhuArea?.fips != null) Usa.counties[jhuArea.fips] else jhuArea?.toAreaInfo()
        return if (areaInfo == null) {
            fine("Area not found: $name")
            fine(name.map { it.toInt() }.toString())
            notFound[name] = UNKNOWN
            null
        } else {
            areaCache[name] = areaInfo
            areaInfo
        }
    }

    /** Get object for area with given FIPS. */
    fun areaByFips(fips: Int) = Usa.counties[fips] ?: Usa.cbsas[fips]

    //endregion

    /** Get FIPS for area with given name. */
    fun fips(name: String) = area(name).fips!!

    /** Get population for area with given name. */
    fun population(name: String) = area(name).metrics.population

    /** Lookup area based on CBSA name. */
    fun cbsa(name: String) = Usa.cbsaByName[name]!!

    //region CHECKS

    private val aliases = Lookup::class.csvResource(true, "resources/area-aliases.csv")
            .map { it.alias to it.name }.toMap()

    private class AreaAlias(val alias: String, val name: String)

    //endregion

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy