R/API_R6.R

# Country R6 class: ----
#' R6 Class representing a country.
#'
#' @description
#' Objects of class Country_R6 provide a chance for the user to call any
#' of the supported APIs for their country of interest. Each instance of
#' class Country_R6 is country-specific, and it follows that the main
#' requirement to create or instantiate a copy of this class is the
#' country's name (see the \code{new} method for an example). While some
#' countries might have more than one name, the Country_R6 class does not
#' have any control over the name accepted by the APIs. Also, it is
#' noteworthy to highlight that the response of the API might differ based
#' on the country name used. The \code{new} method informs the user about
#' the availability of data for their respective country (or country
#' name); therefore, the user can experiment with different names for
#' their country of interest and gage which of the names is globally
#' accepted by the APIs.
#' @format An [R6::R6Class] object.
#' @name Country_R6
NULL
#'
#' @rdname Country_R6
#' @export
Country_R6 <- R6::R6Class(
  # Object name:
  classname = "Country_R6",
  # Public elements:
  public = list(

    #' @field country_code the iso2 and iso3 codes for the country of
    #' interest
    country_code = NULL,
    #' @field data_status bolean for whether data exists in a specific API
    data_status = NULL,
    #' @field dhs_survey_years years of DHS surveys for the country
    dhs_survey_years = NULL,
    #' @field dhs_survey_ids the labels (ids) of DHS surveys for the
    #' country
    dhs_survey_ids = NULL,
    #' @field dhs_indicators the names of indicators available from the
    #' country's DHS surveys
    dhs_indicators = NULL,
    #' @field dhs_data all country DHS data available via the dhs API
    dhs_data = NULL,
    #' @field gho_indicators list of all GHO API indicators
    gho_indicators = NULL,
    #' @field gho_data country GHO API indicator data
    gho_data = NULL,
    #' @field wb_auxiliaries all World Bank auxiliary data
    wb_auxiliaries = NULL,
    #' @field wb_indicators all World Bank indicators available via the
    #' World Bank API
    wb_indicators = NULL,
    #' @field wb_data the indicator specific data retrieved from via the
    #' World Bank API
    wb_data = NULL,
    #' @field health_facilities_data all country-specific health
    #' facilities information fetched from the healthsites.io API
    health_facilities_data = NULL,
    #' @field health_facilities_stats country-specifc health facilities'
    #' numbers by type
    health_facilities_stats = NULL,
    #' @field health_facilities_map a map showing country-specific health
    #' facilities'
    health_facilities_map = NULL,

    #' @description
    #' Initialisation method (triggered when a new object is created).
    #' This function checks whether data is available, allowing users to
    #' quickly see whether or not it is possible to call the API
    #'
    #' @param country_name The name of the country for which the APIs
    #' will be queried.
    #' @param hs_API_key Healthsites API key/token. The API seems to work
    #' with any string passed to it. This behaviour might change in the
    #' future and this method is written to handle calls using valid keys
    #' @param shiny_ Logical, if TRUE, function will suppress some
    #' messages; otherwise, the function will print useful information to
    #' the console
    #' @return A new `Country_R6` object.
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' }
    initialize = function(country_name, hs_API_key = NULL,
                          shiny_ = FALSE) {
      private$country_name <- stringr::str_to_title(country_name)
      # Override " " if a HS API key was supplied:
      if(!is.null(hs_API_key))
        private$hs_API_key <- hs_API_key

      # Check for data via the GHO API:
      ## Grab country id for GHO queries:
      self$country_code['GHO'] <-
        tryCatch({
          tmp <-  private$gho_api_query(
            .query_ = "countryNames",
            .query_args_ = NULL,
            .country_code_ = NULL) %>%
            private$gho_extract_data() %>%
            dplyr::filter(Title %in% private$country_name) %>%
            dplyr::pull(Code)
          # filtering returns no error for countries with no DHS data:
          tmp <- if(length(tmp) > 0) tmp else {
            cat("\n",
                glue::glue("\rGHO: {private$country_name} data is currently unavailable."))
            NA_character_
          }
        }, error = function(e){
          NA_character_
        })
      ## Set data status to TRUE if code/name exists via API:
      self$data_status['GHO'] <-
        if(!is.na(self$country_code['GHO'])) TRUE else FALSE

      # Check if country data is available via the DHS API:
      ## Grab country id for DHS queries:
      self$country_code['DHS'] <-
        tryCatch({
          tmp <- private$dhs_api_query(
            .query_ = 'countryNames',
            .country_code_ = NULL) %>%
            private$dhs_extract_data() %>%
            dplyr::filter(CountryName %in% private$country_name) %>%
            dplyr::pull(DHS_CountryCode)
          # filtering returns no error for countries with no DHS data:
          tmp <- if(length(tmp) > 0) tmp else {
            cat("\n",
                glue::glue("\rDHS: {private$country_name} data is currently unavailable."))
            NA_character_
          }
        }, error = function(e){
          NA_character_
        })
      ## Set data status to TRUE if code/name exists via API:
      self$data_status['DHS'] <-
        if(!is.na(self$country_code['DHS'])) TRUE else FALSE

      # Check for data via the WB API:
      ## Grab country id for WB queries:
      self$wb_auxiliaries <- private$wb_codes_indicators()
      self$country_code['WB'] <-
        tryCatch({
          tmp <-  self$wb_auxiliaries$iso_codes %>%
            dplyr::filter(country %in% private$country_name) %>%
            dplyr::pull(iso2c)
          # filtering returns no error for countries with no DHS data:
          tmp <- if(length(tmp) > 0) tmp else {
            cat("\n",
                glue::glue("\rWB: {private$country_name} data is currently unavailable."))
            NA_character_
          }
        }, error = function(e){
          NA_character_
        })
      ## Set data status to TRUE if code/name exists via API:
      self$data_status['WB'] <-
        if(!is.na(self$country_code['WB'])) TRUE else FALSE

      # Check if country data is available via in the HS API:
      ## Grab country name from HS response:
      self$country_code['HS'] <-
        tryCatch({
          tmp <- private$hs_api_query(
            .query_ = 'country',
            .country_name_ = private$country_name,
            .API_key_ = private$hs_API_key)$
            outputs %>%
            private$hs_extract_values(
              .data_ = .,
              .levels_ = 'label',
              .func_ = 'map_chr',
              .default_ = NA_character_)
          # filtering returns no error for countries with no DHS data:
          tmp <- if(length(tmp) > 0) tmp else {
            cat("\n",
                glue::glue("\rHS: {private$country_name} data is currently unavailable."),
                "\n")
            NA_character_
          }
        }, error = function(e){
          NA_character_
        })
      ## Set data status to TRUE if code/name exists via API:
      self$data_status['HS'] <-
        if(!is.na(self$country_code['HS'])) TRUE else FALSE

      # Inform user (not shiny) which APIs data are available:
      if(!isTRUE(shiny_)) print(self$data_status)

    },

    #' @description
    #' The initial query of user selected APIs
    #'
    #' @param API_name_ A string or vector of strings with the
    #' abbreviation of the API to start querying. Accepted values are
    #' Global Health Observatory (GHO), Demographic and Health Surveys
    #' (DHS), World bank (WB), and Healthsities (HS).
    #'
    #' @return Nothing back to the user, but populates several internal
    #' fields based on the \code{API_name_} passed to the function. These
    #' could include GHO (for example gho_indicators), DHS (for example
    #' dhs_indicators), WB (for example wb_indicators) and HS (for example
    #' health_facilities_data) objects.
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Initiate API queries for HS, DHS, WB, and GHO:
    #' Kenya$
    #'    initiate_APIs(
    #'      API_name_ = c("HS", "DHS", "WB", "GHO")
    #'    )
    #' }
    initiate_APIs = function(API_name_ = c("HS", "DHS", "WB", "GHO")) {
      API_name_ <- stringr::str_to_upper(API_name_)
      supported_APIs <- c("HS", "GHO", "WB", "DHS")
      # Inform the user if requested API is not supported:
      if(!all(API_name_ %in% supported_APIs))
        print(
          paste0(API_name_[!API_name_ %in% supported_APIs],
                 " is unknown or currently unsupported.
             The package currently supports:
             - Global Health Observatory (GHO)
             - Demographic and Health Surveys (DHS)
             - World bank (WB), and
             - Healthsities (HS) API"))
      # Initiate Global Health Observatory (GHO) API query:
      if(any("GHO" %in% API_name_)) {

        if(isTRUE(self$data_status["GHO"])) {
          ## Grab GHO indicators:
          self$initiate_GHO_API()
        }

      }
      # Initiate Demographic and Health Surveys (DHS) API query:
      if(any("DHS" %in% API_name_)) {

        if(isTRUE(self$data_status["DHS"])) {
          ## Fetch all DHS data for the country of interest:
          self$initiate_DHS_API()
        }

      }
      # Initiate World bank (WB) API query:
      if(any("WB" %in% API_name_)) {

        if(isTRUE(self$data_status["WB"])) {
          ## Grab WB indicators:
          self$initiate_WB_API()
        }

      }
      # Initiate Healthsites (HS) API query:
      if(any("HS" %in% API_name_)) {

        if(isTRUE(self$data_status["HS"])) {
          ### Fetch all Healthsites data for the country of interest:
          self$initiate_HS_API()
        }

      }

      invisible(self)
    },

    #' @description
    #' Query Global Health Observatory (GHO) API
    #'
    #' @return Nothing to the user, but populates several fields in the
    #' object calling the method
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Initiate API:
    #' Kenya$
    #'    initiate_GHO_API()
    #' }
    initiate_GHO_API = function() {
      # Initiate Global Health Observatory (GHO) API query:
      if(isTRUE(self$data_status["GHO"]) &
         is.null(self$gho_indicators)) {
        ## Grab GHO indicators:
        self$gho_indicators <- private$gho_api_query(
          .query_ = "indicatorsList",
          .query_args_ = NULL,
          .country_code_ = NULL
        ) %>%
          private$gho_extract_data()

        cat("\n",
            glue::glue("\rGHO: Finished fetching GHO indicators."))
      }

      invisible(self)
    },

    #' @description
    #' Query Demographic and Health Surveys (DHS) API
    #'
    #' @return Nothing to the user, but populates several fields in the
    #' object calling the method
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Initiate API:
    #' Kenya$
    #'    initiate_DHS_API()
    #' }
    initiate_DHS_API = function() {
      # Initiate Demographic and Health Surveys (DHS) API query:
      if(isTRUE(self$data_status["DHS"]) &
         is.null(self$dhs_indicators)) {
        ## Fetch all DHS data for the country interest:
        self$dhs_data <- private$dhs_api_query(
          .query_ = "countryData",
          .country_code_ = self$country_code["DHS"]) %>%
          private$dhs_extract_data()
        ## Save survey years and ids for filtering purposes:
        self$dhs_survey_years <- self$dhs_data %>%
          dplyr::select(SurveyYear) %>%
          dplyr::distinct()
        self$dhs_survey_ids <- self$dhs_data %>%
          dplyr::select(SurveyId) %>%
          dplyr::distinct()
        ## Save available indicators for filtering purposes:
        self$dhs_indicators <- self$dhs_data %>%
          dplyr::select(IndicatorId, Indicator) %>%
          dplyr::distinct()
      }

      invisible(self)
    },

    #' @description
    #' Query World bank (WB) API
    #'
    #' @return Nothing to the user, but populates several fields in the
    #' object calling the method
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Initiate API:
    #' Kenya$
    #'    initiate_WB_API()
    #' }
    initiate_WB_API = function() {
      # Initiate World bank (WB) API query:
      if(isTRUE(self$data_status["WB"]) &
         is.null(self$wb_indicators)) {
        ## Grab WB indicators:
        self$wb_indicators <- self$wb_auxiliaries$indicator_labels

        cat("\n",
            glue::glue("\rWB: Finished fetching WB indicators."))
      }

      invisible(self)
    },

    #' @description
    #' Query Healthsities (HS) API
    #'
    #' @return Nothing to the user, but populates several fields in the
    #' object calling the method
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Initiate API:
    #' Kenya$
    #'    initiate_HS_API()
    #' }
    initiate_HS_API = function() {
      # Initiate Healthsites (HS) API query:
      if(isTRUE(self$data_status["HS"]) &
         is.null(self$health_facilities_data)) {
        ## Query Healthsites API:
        # Set data:
        self$health_facilities_data <- tryCatch(
          expr = {
            tmp <- private$hs_facilities_query() %>%
              private$hs_extract_dataset()

            tmp <- if(length(tmp) > 0) {
              tmp
            } else {
              cat("\n",
                  glue::glue("\rHS: No data available for {private$country_name}.")
              )
              dplyr::tibble(
                " " = glue::glue("No data available for {private$country_name}.")
              )
            }
          }, error = function(e) {
            cat("\n",
                glue::glue("\rHS: No data available for {private$country_name}.")
            )
            dplyr::tibble(
              " " = glue::glue("No data available for {private$country_name}.")
            )
          }
        )
        # Set stats:
        self$health_facilities_stats <- tryCatch(
          expr = {
            tmp <- self$health_facilities_data %>%
              dplyr::count(attributes.amenity)

            tmp <- if(length(tmp) > 0) {
              tmp
            } else {
              dplyr::tibble(
                " " = glue::glue("No data available for {private$country_name}.")
              )
            }
          }, error = function(e) {
            dplyr::tibble(
              " " = glue::glue("No data available for {private$country_name}.")
            )
          }
        )
        # Set map:
        self$health_facilities_map <-
          tryCatch(
            expr = {
              tmp <- private$hs_map_facilities(
                .data_ = self$health_facilities_data)

              tmp <- if(length(tmp) > 0) tmp else NULL
            }, error = function(e) {
              NULL
            }
          )
      }

      invisible(self)
    },

    #' @description
    #' Get data from a specific socio-economic data from the World
    #' Bank
    #'
    #' @param indicator_label WB indicator name or indicator label.
    #' @param date_from Range of data required, start date
    #' @param date_to Range of data required, end date
    #'
    #' @return A tibble with data form the World Bank
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_WB_API()
    #' Kenya$
    #'    set_wb_data(indicator_label = "C1.12")
    #' }
    set_wb_data = function(indicator_label, date_from = 1960,
                           date_to = 2022) {
      indicator_name_ <- self$wb_indicators %>%
        dplyr::filter(indicator == indicator_label)

      if(nrow(indicator_name_) < 1)
        cat("\n",
            glue::glue("\rWB: '{indicator_label}' is currently unrecognised")
        )

      indicator_name_ <- indicator_name_ %>%
        dplyr::pull(name)

      tmp <- private$wb_indicators_data(
        .countries_ = self$country_code['WB'],
        .indicator_id_ = indicator_label,
        .start_ = date_from, .end_ = date_to) %>%
        private$wb_extract_data()

      self$wb_data <-
        if(!is.null(tmp)) {
          tmp
        } else {
          cat("\n",
              glue::glue("\rWB: '{indicator_name_}' is currently unavailable for {private$country_name}.")
          )
          dplyr::tibble(
            " " = glue::glue("'{indicator_name_}' is currently unavailable for {private$country_name}.")
          )
        }

      invisible(self)
    },

    #' @description
    #' Get data from a specific survey
    #'
    #' @param survey DHS survey year.
    #'
    #' @return A tibble containing data from all country-specific DHS
    #' surveys
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_DHS_API()
    #' Kenya$
    #'    get_dhs_survey_data(
    #'       survey = 1989,
    #'       filter_var = "SurveyYear"
    #'    )
    #' }
    get_dhs_survey_data = function(survey) {
      .data_ <- self$dhs_data %>%
        dplyr::filter(SurveyYear %in% survey)

      if(nrow(.data_) < 1) {
        cat("\n",
            glue::glue("\rDHS: '{survey}' is currently unrecognised. Current possible survey years include: {self$dhs_survey_years}"),
            "\n"
        )
      }

      return(.data_)
    },

    #' @description
    #' Get data for a specific health indicator from DHS
    #'
    #' @param indicator_name DHS indicator name or indicator label.
    #' @param filter_var Either IndicatorId (default if nothing was
    #' supplied) or Indicator. This variable is used by the function to
    #' filter the data accordingly.
    #'
    #' @return A tibble containing indicator-specific data from the
    #' country's DHS surveys
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_DHS_API()
    #' Kenya$
    #'    get_dhs_ind_data(
    #'       indicator_name = "FE_FRTR_W_A15",
    #'       filter_var = "IndicatorId"
    #'    )
    #' }
    get_dhs_ind_data = function(indicator_name, filter_var) {
      if(is.null(filter_var))
        filter_var <- 'Indicator'
      if(!any(filter_var %in% c('IndicatorId', 'Indicator')))
        stop("the supplied Indicator filtering variable is unknown")

      .data_ <- self$dhs_data %>%
        dplyr::filter(.data[[filter_var]] %in% indicator_name)

      if(nrow(.data_) < 1) {
        cat("\n",
            glue::glue("\rDHS: '{indicator_name}' is currently unrecognised."),
            "\n"
        )
      }

      .data_ <- .data_ %>%
        dplyr::group_by(.data[[filter_var]]) %>%
        dplyr::slice_max(SurveyYear) %>%
        dplyr::filter()

      return(.data_)
    },

    #' @description
    #' Get data from a specific socio-economic data from the World Bank
    #'
    #' @return A tibble containing the country indicator-specific data
    #' fetched from the World Bank API
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_WB_API()
    #' Kenya$
    #'    set_wb_data(indicator_label = "C1.12")$
    #'    get_wb_ind_data()
    #' }
    get_wb_ind_data = function() {

      return(self$wb_data)
    },

    #' @description
    #' Get health facilities' data
    #'
    #' @return A tibble containing unmodified country-specific health
    #' facilities information
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_HS_API()
    #' Kenya$
    #'    get_facilities_data()
    #' }
    get_facilities_data = function() {

      return(self$health_facilities_data)
    },

    #' @description
    #' Get health facilities' counts
    #'
    #' @return A tibble containing country-specific health facilities
    #' numbers/stats by facility type
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_HS_API()
    #' Kenya$
    #'    get_facilities_stats()
    #' }
    get_facilities_stats = function() {

      return(self$health_facilities_stats)
    },

    #' @description
    #' Get health facilities' map
    #'
    #' @return An object of class leaflet (A leaflet map)
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_HS_API()
    #' Kenya$
    #'    get_facilities_map()
    #' }
    get_facilities_map = function() {

      return(self$health_facilities_map)
    },

    #' @description
    #' Get the indicators from the GHO API
    #'
    #' @return a tibble of GHO indicators (names and codes).
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_GHO_API()
    #' Kenya$
    #'    get_gho_ind_list()
    #' }
    get_gho_ind_list = function() {

      return(self$gho_indicators)
    },

    #' @description
    #' Get country-specific indicator data for the GHO API
    #'
    #' @param indicator_code_ the GHO code used to refer to the indicator
    #' of interest
    #'
    #' @return A tibble containing the indicator name, label, and value
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class Country_R6:
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #' Kenya$
    #'    initiate_GHO_API()
    #' Kenya$
    #'     get_gho_ind_data(
    #'       indicator_code_ = "WHOSIS_000001"
    #'     )
    #' }
    get_gho_ind_data = function(indicator_code_) {
      indicator_name_ <- self$gho_indicators %>%
        dplyr::filter(IndicatorCode == indicator_code_[1])

      if(nrow(indicator_name_) < 1) {
        cat("\n",
            glue::glue("\rGHO: '{indicator_code_}' is currently unrecognised."),
            "\n"
        )
      }

      indicator_name_ <- indicator_name_ %>%
        dplyr::pull(IndicatorName)

      API_response <- private$gho_api_query(
        .query_ = "countryIndicatorData",
        .query_args_ = list('indc_label_' = indicator_code_[1]),
        .country_code_ = self$country_code['GHO']
      )

      if(!is.null(API_response))
        data_ <- if(length(API_response$outputs$value) == 0){
          cat("\n",
              glue::glue("\rGHO: '{indicator_name_}' is currently unavailable for {private$country_name}.")
          )
          dplyr::tibble(
            " " = glue::glue("'{indicator_name_}' is currently unavailable for {private$country_name}.")
          )
        } else {
          private$gho_extract_data(API_response)
        }

      # Some indicators have two or more codes (Ambient air pollution
      # attributable DALYs):
      if(length(indicator_code_) > 1) {
        for (i in 2:length(indicator_code_)) {
          API_response <- private$gho_api_query(
            .query_ = "countryIndicatorData",
            .query_args_ = list('indc_label_' = indicator_code_[i]),
            .country_code_ = self$country_code['GHO']
          )

          if(!is.null(API_response))
            data_ <- if(length(API_response$outputs$value) == 0){
              data_
            } else {
              data_ %>%
                dplyr::bind_rows(
                  private$gho_extract_data(API_response)
                )
            }
        }
      }

      self$gho_data <- data_

      return(data_)
    }
  ),

  # Private elements:
  private = list(

    hs_API_key = "key", # healthsites API key, currently accepts anything.
    country_name = NULL, # name of country for which the copy was made.

    # Query the DHS API
    # Website: (https://dhsprogram.com/)
    #
    # .query_ the name of the query to be executed.
    # .country_code_ the country of interest's iso2 code.
    # .page_ the API page number from which data is to be fetched.
    #
    # A list of objects including the raw response from the API
    # in addition to parsed results.
    dhs_api_query = function(.query_,
                             .country_code_ = self$country_code['DHS'],
                             .page_ = 1) {
      # Append query arguments together:
      url_ <- function(page_ = .page_, query_ = .query_) {
        blueprint <- switch(
          query_,
          countryNames = "https://api.dhsprogram.com/rest/dhs/countries?returnFields=CountryName,DHS_CountryCode&f=json",
          countryData = "https://api.dhsprogram.com/rest/dhs/data/{.country_code_}?page={page_}"
        )

        return(glue::glue(blueprint))
      }

      # Send request to API:
      response <- httr::GET(url = url_())
      httr::stop_for_status(response) # abort if request was unsuccessful
      # Get results:
      headers <- httr::headers(response)
      # Parse results:
      ## Identify response encoding:
      encoding <- stringi::stri_enc_detect(
        httr::content(x = response, as = "raw"))[[1]][1, 1]
      ## Parse response as text:
      parsed_text <- httr::content(x = response, as = "text",
                                   encoding = encoding)
      ## Identify content type:
      content_type <- response$headers$`content-type`
      ## Parse contents:
      if(grepl(content_type, pattern = "json")) {
        ### explicitly if of type json:
        parsed_response <- RJSONIO::fromJSON(parsed_text)
      } else {
        ### automatically by httr:
        parsed_response <- httr::content(x = response, as = "parsed",
                                         encoding = encoding)
      }
      # Grab other pages, if additional ones exist:
      if(!is.null(parsed_response[['TotalPages']])) {
        tot_pages_ <- parsed_response[['TotalPages']]
        if(parsed_response[['TotalPages']] > 1) {
          pages_ <- 2:tot_pages_
          names(pages_) <- paste("page", pages_)
          env_ <- environment()
          cat("\n")
          purrr::walk(
            .x = pages_,
            .f = function(page_) {
              cat(paste0("\rDHS: ", .country_code_, " - fetching page ",
                         page_, " of ", tot_pages_))
              # Send request to API:
              response_ <- httr::GET(url = url_(page_ = page_))
              httr::stop_for_status(response_)
              # Parse results:
              ## Identify response encoding:
              encoding_ <- stringi::stri_enc_detect(
                httr::content(x = response_, as = "raw"))[[1]][1, 1]
              ## Parse response as text:
              parsed_text_ <- httr::content(x = response_, as = "text",
                                            encoding = encoding_)
              ## Identify content type:
              content_type_ <- response_$headers$`content-type`
              ## Parse contents:
              if(grepl(content_type_, pattern = "json")) {
                ### explicitly if of type json:
                parsed_response_ <- RJSONIO::fromJSON(parsed_text_)
              } else {
                ### automatically by httr:
                parsed_response_ <- httr::content(x = response_,
                                                  as = "parsed",
                                                  encoding = encoding_)
              }
              assign('response',
                     purrr::map2(
                       .x = response,
                       .y = response_,
                       .f =  ~ c(.x, .y)
                     ),
                     envir = env_)
              assign('parsed_response',
                     purrr::map2(
                       .x = parsed_response,
                       .y = parsed_response_,
                       .f =  ~ c(.x, .y)
                     ),
                     envir = env_)
            }
          )
        }
      }
      # Pack results into one list:
      results <- list('outputs' = parsed_response, 'response' = response,
                      'headers' = headers, 'encoding' = encoding)

      return(results)
    },

    # Extract data retrieved from the DHS API
    #
    # .data_ Extraction target object. A list of class
    # dhs_api_data that contains the data retrieved using the
    # dhs_api_query function.
    #
    # A tibble containing the data retrieved via the API
    dhs_extract_data = function(.data_) {
      data_ <- .data_$outputs$Data %>%
        purrr::pmap_df(.l = ., .f = function(...) c(...))

      return(data_)
    },

    # Query the Global Health Observatory (GHO) api
    # website: (https://www.who.int/data/gho/info/gho-odata-api)
    #
    # .query_ the name of the query to be executed.
    # .country_code_ the country of interest's iso2 code.
    # .page_ the API page number from which data is to be fetched.
    #
    # A list of objects including the raw response from the API
    # in addition to parsed results.
    #
    # ghoDimensions = gho_api_query(
    #   .query_ = "dimensions",
    #   .country_code_ = NULL)
    # ghoCountryNames = gho_api_query(
    #   .query_ = "countryNames",
    #   .country_code_ = NULL)
    # ghoIndicators = gho_api_query(
    #   .query_ = "indicatorsList",
    #   .country_code_ = NULL)
    # ghoIndicatorInfo = gho_api_query(
    #   .query_ = "grabIndicatorInfo",
    #   .query_args_ =
    #     list('indc_name_' = "Ambient%20air%20pollution%20attributable%20deaths"),
    #   .country_code_ = NULL)
    # ghoIndicatorSearch = gho_api_query(
    #   .query_ = "searchIndicators",
    #   .query_args_ = list('indc_key_words_' = "Household"),
    #   .country_code_ = NULL)
    # ghoIndicatorData = gho_api_query(
    #   .query_ = "indicatorData",
    #   .query_args_ = list('indc_label_' = "WHOSIS_000001"),
    #   .country_code_ = NULL)
    # ghoCountryIndicatorData = gho_api_query(
    #   .query_ = "countryIndicatorData",
    #   .query_args_ = list('indc_label_' = "WHOSIS_000001"),
    #   .country_code_ = "KEN")
    gho_api_query = function(.query_,
                             .query_args_ = list(NULL),
                             .country_code_) {# = self$country_code['GHO']
      # Append query arguments together:
      url_ <- function(query_ = .query_,
                       country_code_ = .country_code_,
                       indc_name_ = .query_args_[['indc_name_']],
                       indc_key_words_ = .query_args_[['indc_key_words_']],
                       indc_label_ = .query_args_[['indc_label_']]) {
        blueprint <- switch(
          query_,
          dimensions = "https://ghoapi.azureedge.net/api/Dimension/",
          countryNames = "https://ghoapi.azureedge.net/api/DIMENSION/COUNTRY/DimensionValues",
          indicatorsList = "https://ghoapi.azureedge.net/api/Indicator/",
          grabIndicatorInfo = "https://ghoapi.azureedge.net/api/Indicator?$filter=IndicatorName%20eq%20'{indc_name_}'",
          searchIndicators = "https://ghoapi.azureedge.net/api/Indicator?$filter=contains(IndicatorName,'{indc_key_words_}')",
          indicatorData = "https://ghoapi.azureedge.net/api/{indc_label_}",
          countryIndicatorData = "https://ghoapi.azureedge.net/api/{indc_label_}?$filter=SpatialDim%20eq%20'{country_code_}'"
        )

        return(glue::glue(blueprint))
      }
      # Send request to API:
      response <- httr::GET(url = url_())
      e = tryCatch({
        httr::stop_for_status(response) # abort if request was unsuccessful
        NULL
      }, error = function(e) {

        return(list('outputs' = NULL, 'error' = e, 'url' = url_()))
      })
      # Parse results:
      ## Identify response encoding:
      encoding <- stringi::stri_enc_detect(
        httr::content(x = response, as = "raw"))[[1]][1, 1]
      ## Parse response as text:
      parsed_text <- httr::content(x = response, as = "text",
                                   encoding = encoding)
      ## Identify content type:
      content_type <- response$headers$`content-type`
      ## Parse contents:
      if(grepl(content_type, pattern = "json")) {
        ### explicitly if of type json:
        parsed_response <- RJSONIO::fromJSON(parsed_text)
      } else {
        ### automatically by httr:
        parsed_response <- httr::content(x = response, as = "parsed",
                                         encoding = encoding)
      }
      # Pack results into one list:
      results <- list('outputs' = parsed_response, 'error' = e,
                      'url' = url_())

      return(results)
    },

    # Extract data from the GHO API
    #
    # .data_ Extraction target object. A list of class
    # gho_api_data that contains the data retrieved using the
    # gho_api_query function.
    #
    # A tibble containing the data retrieved via the API.
    #
    # ghoCountryNames = gho_api_query(
    #   .query_ = "countryNames",
    #   .country_code_ = NULL)
    #
    # ghoCountries = gho_extract_data(.data_ = ghoCountryNames)
    gho_extract_data = function(.data_) {
      data_ <- .data_$outputs$value %>%
        purrr::transpose() %>%
        purrr::pmap_dfr(.l = ., .f = function(...) dplyr::bind_rows(...))

      return(data_)
    },

    # Query the Health sites API
    # website: (https://healthsites.io/)
    #
    # .query_ The name of the query to be executed.
    # .API_key_ A string of characters required to access the API
    # To get one:
    # 1. Establish an OpenStreetMap account:
    # https://www.openstreetmap.org/user/new
    # 2. Sign into Healthsites with your OSM account:
    # https://healthsites.io/map
    # 3. Get an API token off your profile page:
    # - Click on your user profile to open your profile page
    # - There you will find an option to generate/view the API-key/token
    # .country_name_ the name of the country for which the API
    # will be queried.
    # .page_ the API page number from which data is to be fetched
    #
    # A list of objects including the raw response from the API
    # in addition to parsed results.
    hs_api_query = function(.query_,
                            .country_name_ = private$country_name,
                            .page_ = 1,
                            .API_key_ = private$hs_API_key) {
      # Append query arguments together:
      url_ <- function(page_ = .page_, query_ = .query_) {
        blueprint <- switch(
          query_,
          country = "https://healthsites.io/api/v2/countries/autocomplete?api-key={.API_key_}&q={.country_name_}",
          facilities = "https://healthsites.io/api/v2/facilities/?api-key={.API_key_}&page={page_}&country={.country_name_}"
        )

        return(glue::glue(blueprint))
      }
      # Send request to API:
      response <- httr::GET(url = url_())
      httr::stop_for_status(response) # abort if request was unsuccessful
      # Get results:
      headers <- httr::headers(response)
      # Parse results:
      ## Identify response encoding:
      encoding <- stringi::stri_enc_detect(
        httr::content(x = response, as = "raw"))[[1]][1, 1]
      ## Parse response as text:
      parsed_text <- httr::content(x = response, as = "text",
                                   encoding = encoding)
      ## Identify content type:
      content_type <- response$headers$`content-type`
      ## Parse contents:
      if(grepl(content_type, pattern = "json")) {
        ### explicitly if of type json:
        parsed_response <- RJSONIO::fromJSON(parsed_text)
      } else {
        ### automatically by httr:
        parsed_response <- httr::content(x = response, as = "parsed",
                                         encoding = encoding)
      }
      # Pack results into one list:
      results <- list('outputs' = parsed_response, 'response' = response,
                      'headers' = headers, 'encoding' = encoding)

      return(results)
    },

    # Query healthsities API for all existing facilities in a country
    #
    # .country_name_ The name of the country to query the API
    # .API_key_ Healthsities API key or token.
    #
    # A list of objects from recursive querying including the raw
    # response from the API in addition to parsed results.
    hs_facilities_query = function(.country_name_ = private$country_name,
                                   .API_key_ = private$hs_API_key) {
      loop_ <- TRUE
      page_ <- 1
      cat(paste0("\n\rHS: ", .country_name_, " - fetching page ", page_))
      # retrieve first page of facilities:
      outData_ <- private$hs_api_query(
        .query_ = 'facilities',
        .country_name_ = .country_name_,
        .page_ = page_,
        .API_key_ = .API_key_
      )
      # prevent looping if all available facilities were grabbed:
      if(length(outData_$outputs) < 100) loop_ <- FALSE
      # otherwise grab facilities in next page(s):
      page_ <- page_ + 1
      while (loop_) {
        cat(paste0("\rHS: ", .country_name_, " - fetching page ", page_))
        # use trycatch() to exit function with available data:
        tryCatch(
          {
            # query api for next page of facilities:
            inData_ <- private$hs_api_query(
              .query_ = 'facilities',
              .country_name_ = .country_name_,
              .page_ = page_,
              .API_key_ = .API_key_
            )
            # merge outputs:
            outData_ <- purrr::map2(outData_, inData_, ~ c(.x, .y))
            # exit loop if all facilities were downloaded:
            if(length(inData_$outputs) == 0 |
               length(inData_$outputs) < 100) loop_ <- FALSE
          },
          error=function(cond) {
            # exit loop:
            loop_ <- FALSE
          }
        )

        page_ <- page_ + 1
      }

      return(outData_)
    },

    # Extract data from the healthsites API
    #
    # .data_ Extraction target object. A list of class hs_api_data
    # that contains the data retrieved using the hs_xxxx_query functions
    #
    # A tibble containing facility information and spatial data
    # in the form of simple lng/lat data
    # These data can be passed to leaflet (lng/lat)
    # functions to map the facilities appropriately.
    hs_extract_dataset = function(.data_) {
      .data_ <- .data_$outputs %>%
        # expose the data in the outputs list:
        purrr::transpose() %>%
        # extract the contents and put them a tibble:
        purrr::pmap_df(.l = ., .f = function(...) c(...)) %>%
        # pivot spatial data wider to create lng/lat columns:
        dplyr::mutate(coordinates_labels = rep(c('lng', 'lat'), nrow(.)/2)) %>%
        tidyr::pivot_wider(names_from = coordinates_labels,
                           values_from = centroid.coordinates) %>%
        dplyr::select(-c(dplyr::contains('coordinates'),
                         dplyr::contains('centroid'))) %>%
        dplyr::select(c(attributes.amenity, attributes.name,
                        attributes.changeset_id, attributes.healthcare,
                        lng, lat))

      return(.data_)
    },

    # Extract data from the healthsites API
    #
    # .data_ Extraction target object. A list of class hs_api_data
    # that contains the data retrieved using one of the hs_xxxx_query
    # functions.
    # .levels_ Passed to function purrr::pluck. A list of
    # accessors for indexing into the object. Can be an integer position,
    # a string name, or an accessors function (except for the assignment
    # variants which only support names and positions). If the object
    # being indexed is an S4 object, accessing it by name will return
    # the corresponding slot.
    # .default_ Passed to function purrr::pluck. It is a value to
    # use if target is empty or absent.
    # .func_ A function from the purrr::map/purrr::pmap family to
    # be used in extracting values from the target. If no function was
    # supplied, the supplied function was not expected or failed to
    # execute, then purrr::map is used.
    #
    # A vector, list or dataset based on the supplied function
    # and the levels of extraction.
    hs_extract_values = function(.data_, .levels_, .default_ = NULL,
                                 .func_ = NULL) {
      # use the default function "map" if non were supplied:
      if(!is.null(.func_)) {
        # in case an unknown map_ function was supplied, fall back to default:
        tryCatch(
          {
            purrr::exec(
              .fn = .func_,
              .data_, purrr::pluck, !!!.levels_, .default = .default_)
          },
          error = function(e) {
            message(paste0("Unable to run the supplied function [",
                           .func_,
                           "]. Using default function."))
            purrr::map(
              .data_, purrr::pluck, !!!.levels_,
              .default = .default_)
          }
        )
      } else {
        purrr::map(
          .data_, purrr::pluck, !!!.levels_,
          .default = .default_)
      }
    },

    # Visualise country data on a map
    #
    # .data_ A tibble containing facility information and spatial
    # data in the form of (lng/lat) data.
    # ... Extra arguments passed to the leaflet::leaflet map.
    #
    # An object of class leaflet (A leaflet map).
    hs_map_facilities = function(.data_, ...) {
      if(nrow(.data_) > 1) {
        basemap <- leaflet::leaflet(.data_) %>%
          # add different provider tiles
          leaflet::addProviderTiles(
            provider = "OpenStreetMap",
            group = "OpenStreetMap") %>% # give the layer a name
          leaflet::addProviderTiles(
            provider = "CartoDB.Positron",
            group = "CartoDB.Positron") %>%
          leaflet::addProviderTiles(
            provider = "CartoDB.DarkMatter",
            group = "CartoDB.DarkMatter") %>%
          # add a layers control
          leaflet::addLayersControl(
            baseGroups = c("OpenStreetMap",  "CartoDB.Positron",
                           "CartoDB.DarkMatter"),
            overlayGroups = c("dots","icons","icons clustered"),
            # position it on the topleft
            position = "topleft")

        #### Icons and labels;
        colours <- c("red", "orange", "beige", "green", "blue", "purple",
                     "pink", "cadetblue", "gray")

        icons <- leaflet::awesomeIcons(
          icon = "medkit",
          iconColor = "white",
          library = "ion",
          markerColor =
            colours[match(
              .data_$attributes.amenity,
              levels(as.factor(.data_$attributes.amenity)))] %>%
            {ifelse(is.na(.), "black", .)})

        labels <- sprintf(
          "<strong> %s </strong> <br/> %s",
          .data_$attributes.name,
          .data_$attributes.amenity) %>%
          purrr::map(htmltools::HTML)

        makeLegend = function(map, colors, labels, shapes  = "square",
                              sizes = 30,
                              borders ="black"){
          legend_colors = paste0(
            colors, "; width:", sizes, "px; height:",
            sizes, "px; border:3px solid ", borders,
            "; border-radius:", shapes)
          legend_labels = paste0(
            "<div style='display: inline-block;height: ",
            sizes, "px;margin-top: 4px;line-height: ",
            sizes, "px;'>", labels, "</div>")

          return(
            leaflet::addLegend(
              map, colors = legend_colors,
              labels = legend_labels,
              group = 'Legend')
          )
        }


        map <- basemap %>%
          leaflet::addCircleMarkers(
            lng = ~ lng,
            lat = ~ lat,
            label = ~ labels,
            radius = 2,
            group = "dots",
            color = colours[match(
              .data_$attributes.amenity,
              levels(as.factor(.data_$attributes.amenity)))] %>%
              {ifelse(is.na(.), "black", .)}) %>%
          leaflet::addAwesomeMarkers(
            lng = ~ lng,
            lat = ~ lat,
            icon = icons,
            label = ~ labels,
            group = "icons clustered",
            clusterOptions = leaflet::markerClusterOptions()) %>%
          leaflet::addAwesomeMarkers(
            lng = ~ lng,
            lat = ~ lat,
            icon = icons,
            group = "icons",
            label = ~ labels) %>%
          makeLegend(
            colors =
              colours[1:length(levels(
                as.factor(.data_$attributes.amenity)))],
            labels =
              levels(
                as.factor(.data_$attributes.amenity)),
            shapes = "circles",
            sizes = 15,) %>%
          leaflet::hideGroup("icons") %>%
          leaflet::hideGroup("dots")

        return(map)
      } else NULL

    },

    # Save World Bank API indicators
    #
    # .existing_ If TRUE (default), extract indicators from the
    # WDI::WDI_data package. Otherwise, this function uses
    # WDI::WDIcache() to download data via the World Bank API.
    #
    # A list containing two tibbles; one with World Bank API
    # country names, iso2 and iso3 codes, while the second one contains
    # indicator names and labels.
    wb_codes_indicators = function(.existing_ = TRUE) {
      if(.existing_){
        list_ <- list(
          'iso_codes' = WDI::WDI_data$country %>%
            dplyr::as_tibble() %>%
            dplyr::select(country, iso3c, iso2c),
          'indicator_labels' = WDI::WDI_data$series %>%
            dplyr::as_tibble() %>%
            dplyr::select(indicator, name)
        )
      } else {
        cache_ <- WDI::WDIcache()
        list_ <- list(
          'iso_codes' = cache_$country %>%
            dplyr::as_tibble() %>%
            dplyr::select(country, iso3c, iso2c),
          'indicator_labels' = cache_$series %>%
            dplyr::as_tibble() %>%
            dplyr::select(indicator, name)
        )
      }

      return(list_)
    },

    # Query the World Bank API for indicator data
    # Website: (https://datahelpdesk.worldbank.org/knowledgebase)
    #
    # .countries_ The iso2code of the country or countries to
    # query.
    # .indicator_id_ Id of the indicator to query.
    # .start_ Year of earliest records to query; earliest is 1960.
    # .end_ Year of latest records to query.
    # .latest_ Number of latest records to return, default is 1.
    # .per_page_ Number of records per page, default/maximum is
    # 32500.
    # .page_ The page number from which to start querying the api.
    #
    # A list of objects including the raw response from the API
    # in addition to parsed results.
    wb_indicators_data = function(.countries_, .indicator_id_,
                                  .start_ = 1960, .end_ = 2022,
                                  .latest_ = 1, .per_page_ = 32500,
                                  .page_ = 1) {

      # Allow to query several countries:
      country_ids_ <- .countries_[1]
      if(length(.countries_) > 1) {
        for (i in 2:length(.countries_)) {
          country_ids_ <- paste0(country_ids_, ";", .countries_[i])
        }
      }

      # Set query url:
      url_ <- function(page_ = .page_)
        glue::glue("https://api.worldbank.org/v2/en/country/{country_ids_}/indicator/{.indicator_id_}?format=json&date={.start_}:{.end_}&per_page={.per_page_}&page={page_}")

      # Send request to API:
      response <- httr::GET(url = url_())
      httr::stop_for_status(response) # abort if request was unsuccessful
      # Parse results:
      ## Identify response encoding:
      encoding <- stringi::stri_enc_detect(
        httr::content(x = response, as = "raw"))[[1]][1, 1]
      ## Parse response as text:
      parsed_text <- httr::content(x = response, as = "text",
                                   encoding = encoding)
      ## Identify content type:
      content_type <- response$headers$`content-type`
      ## Parse contents:
      if(grepl(content_type, pattern = "json")) {
        ### explicitly if of type json:
        parsed_response <- RJSONIO::fromJSON(parsed_text)
      } else {
        ### automatically by httr:
        parsed_response <- httr::content(x = response, as = "parsed",
                                         encoding = encoding)
      }

      # Grab other pages, if more exist:
      if(!is.null(parsed_response[[1]][['pages']])) {
        tot_pages <- parsed_response[[1]][['pages']]
        if(tot_pages > 1) {
          pages_ <- 2:tot_pages
          names(pages_) <- paste("page", pages_)
          env_ <- environment()
          cat("\n")
          purrr::walk(
            .x = pages_,
            .f = function(page_) {
              cat(
                glue::glue("\rWB: fetching page {page_} of {tot_pages}"))
              # Send request to API:
              response_ <- httr::GET(url = url_(page_ = page_))
              httr::stop_for_status(response_)
              # Parse results:
              ## Identify response encoding:
              encoding_ <- stringi::stri_enc_detect(
                httr::content(x = response_, as = "raw"))[[1]][1, 1]
              ## Parse response as text:
              parsed_text_ <- httr::content(x = response_, as = "text",
                                            encoding = encoding_)
              ## Identify content type:
              content_type_ <- response_$headers$`content-type`
              ## Parse contents:
              if(grepl(content_type_, pattern = "json")) {
                ### explicitly if of type json:
                parsed_response_ <- RJSONIO::fromJSON(parsed_text_)
              } else {
                ### automatically by httr:
                parsed_response_ <- httr::content(x = response_,
                                                  as = "parsed",
                                                  encoding = encoding_)
              }
              assign('response',
                     purrr::map2(
                       .x = response,
                       .y = response_,
                       .f =  ~ c(.x, .y)
                     ),
                     envir = env_)
              assign('parsed_response',
                     purrr::map2(
                       .x = parsed_response,
                       .y = parsed_response_,
                       .f =  ~ c(.x, .y)
                     ),
                     envir = env_)
            }
          )
        }
      }

      # Pack results into one list:
      results <- list('outputs' = parsed_response, 'response' = response,
                      'encoding' = encoding)

      return(results)
    },

    # Extract data retrieved from the World Bank api
    #
    # .data_ Extraction target object. A list of class
    # wb_api_data that contains the data retrieved by the
    # wb_indicators_data function.
    #
    # A tibble containing the data retrieved via the api.
    wb_extract_data = function(.data_) {
      # Indicators values are missing for some countries,
      # tryCatch() should help prevent errors in such occasions:
      .data_ <- tryCatch({
        .data_$outputs[[2]] %>%
          purrr::transpose() %>%
          purrr::pmap_df(.l = ., .f = function(...) c(...)) %>%
          dplyr::filter(!is.na(value))
      }, error = function(e) {
        NULL
      })

      return(.data_)
    }

  )
)

# World R6 class: ----
#' R6 Class representing a world.
#'
#' @description
#' An instance or object of class world is expected to contain (house)
#' objects from class \link{Country_R6} The methods in this class should
#' merge the data from all added objects and summarise/present them.
#' @format An [R6::R6Class] object.
#' @name World_R6
NULL

#' @rdname World_R6
#' @export
#'
#' @examples
#' \dontrun{
#' # Instantiate a copy of class World_R6:
#' World <- World_R6$new()
#' }
World_R6 <- R6::R6Class(
  # Object name:
  classname = "World",
  # Public elements:
  public = list(

    #' @field country_name the name of the country being added. This name
    #' should be the one used in creating the Country_R6 object.
    country_name = vector(),
    #' @field country_data a list housing (to house) clones (copies) of
    #' the instance (object) of class Country_R6 being added.
    country_data = list(),
    #' @field world_data a tibble containing data from all added countries
    world_data = dplyr::tibble(
      'attributes.amenity' = NA, 'attributes.name' = NA, 'lng' = NA,
      'lat' = NA),
    #' @field world_map an object of class leaflet (A leaflet map).
    world_map = NULL,
    #' @field world_stats a tibble containing the counts of health
    #' facilities (by type)
    world_stats = NULL,

    #' @description
    #' Add country object (of class Country_R6) to this object
    #'
    #' @param country_name The name of the country to which the
    #' country_object will be associated
    #' @param country_object An object, instance of class
    #' \link{Country_R6} that contain the data from the APIs for the added
    #' country.
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate a copy of class Country_R6 in the global environment:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' Kenya <- Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())
    #'
    #' # Query the healthsities API:
    #' Kenya$
    #'     query_health_facilities()
    #'
    #' # Add a clone of the Country_R6 object \code{Kenya} to the World_R6:
    #' object \code{World}
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Kenya)
    #'
    #' # Country_R6 object do not need to be created in the global env.
    #' # Below we create an object of class Country_R6 while adding it to
    #' # World_R6 object:
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #' }
    add_country = function(country_name, country_object) {
      names_ <- objects_ <- list()
      # avoid problems if only one country name/object were supplied:
      if(length(country_name) == 1) {
        names_[[country_name]] <- country_name
        objects_[[country_name]] <- country_object
      } else {
        names_ <- country_name
        objects_ <- country_object
      }
      # allow several countries to be added at once:
      purrr::walk2(
        .x = names_,
        .y = objects_,
        .f = function(name_, object_) {
          self$country_name[[name_]] <- name_
          self$country_data[[name_]] <- object_
        }
      )

      invisible(self)
    },

    #' @description
    #' Update World data on health sites. This method is useful after an
    #' object of class Country_R6 health facilities were queried
    #'
    #' @param country_name The name of the country for which the method
    #' will update the world's data.
    #'
    #' @return Nothing back to the user, but updates world_data, world_map
    #' and world_stats.
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate a copy of class Country_R6 within the World object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Update world data:
    #' World$update_world_data(country_name = "Kenya")
    #' }
    update_world_data = function(country_name) {
      names_ <- list()
      # avoid problems if only one country name was supplied:
      if(length(country_name) == 1) {
        names_[[country_name]] <- country_name
      } else {
        names_ <- country_name
      }
      # allow several countries to be added at once:
      purrr::walk(
        .x = names_,
        .f = function(name_) {
          self$world_data <- tryCatch(
            expr = {
              if(nrow(self$
                      country_data[[name_]]$
                      health_facilities_data) > 1) {
                dplyr::bind_rows(
                  self$world_data,
                  self$country_data[[name_]]$health_facilities_data %>%
                    dplyr::mutate('country' = name_) %>%
                    dplyr::distinct()
                )
              } else {
                self$world_data
              }
            }, error = function(e) {
              self$world_data
            }
          )
        }
      )
      # compute/recompute stats:
      self$world_stats <- tryCatch(
        expr = {
          self$world_data %>%
            dplyr::filter(!is.na(country)) %>%
            dplyr::count(country, attributes.amenity)
        }, error = function(e) {
          self$world_stats
        }
      )
      # draw/redraw map:
      self$world_map <- private$hs_map_world_facilities(self$world_data)

      invisible(self)
    },

    #' @description
    #' Get data from a specific country in the World_R6 class
    #'
    #' @param country_name The name of the country for which the method
    #' will retrieve the data.
    #'
    #' @return A tibble with the specified country health sites data
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate a copy of class Country_R6 within the World object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Request some of Kenya's data back:
    #' World$get_country_data(country_name = "Kenya")
    #' }
    get_country_data = function(country_name) {
      data = self$country_data[[country_name]]$health_facilities_data

      return(data)
    },

    #' @description
    #' Get the map showing health facilities in a specific country.
    #'
    #' @param country_name The name of the country for which the method
    #' will retrieve the map
    #'
    #' @return An object of class leaflet (A leaflet map)
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate a copy of class Country_R6 within the World object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Request Kenya's health facilities map:
    #' World$get_country_map(country_name = "Kenya")
    #' }
    get_country_map = function(country_name) {
      map = self$country_data[[country_name]]$health_facilities_map

      return(map)
    },

    #' @description
    #' Get the map showing health facilities in a all added country.
    #'
    #' @return An object of class leaflet (A leaflet map)
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate two objects of class Country_R6 within the World
    #' object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #' World$add_country(
    #'       country_name = "Zambia",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Request available (of all included countries) health facilities
    #' data:
    #' World$get_world_data(country_name = "Kenya")
    #' }
    get_world_data = function() {
      data <- self$world_data

      return(data)
    },

    #' @description
    #' Get the counts/numbers of health facilities by country name and
    #' facility type.
    #'
    #' @return A tibble containing the request stats
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate two objects of class Country_R6 within the World
    #' object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #' World$add_country(
    #'       country_name = "Zambia",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Request available (of all included countries) health facilities
    #' stats:
    #' World$get_world_stats(country_name = "Kenya")
    #' }
    get_world_stats = function() {
      data <- self$world_stats

      return(data)
    },

    #' @description
    #' Get a map showing health facilities in all added countries
    #'
    #' @return An object of class leaflet (A leaflet map)
    #' @export
    #'
    #' @examples
    #' \dontrun{
    #' # Instantiate a copy of class World_R6:
    #' World <- World_R6$new()
    #'
    #' # Instantiate two objects of class Country_R6 within the World
    #' object:
    #' # Note: the following example assumes that the user had set their
    #' # healthsites API key into their environment using set_hs_API_key()
    #' World$add_country(
    #'       country_name = "Kenya",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #' World$add_country(
    #'       country_name = "Zambia",
    #'       country_object = Country_R6$new(
    #'                country_name = "Kenya",
    #'                hs_API_key = get_hs_API_key())$
    #'       query_health_facilities()
    #'       )
    #'
    #' # Request map of available health facilities from all countries
    #' added to the World object
    #' :
    #' World$get_world_map(country_name = "Kenya")
    #' }
    get_world_map = function() {
      map <- self$world_map

      return(map)
    }
  ),
  # Private elements:
  private = list(

    hs_map_world_facilities = function(.data_, ...) {
      # remove rows with where all values are missing:
      .data_ <- .data_ %>%
        dplyr::filter(!is.na(country))
      # build base leaflet map:
      basemap <- leaflet::leaflet(.data_) %>%
        # add different provider tiles
        leaflet::addProviderTiles(
          provider = "OpenStreetMap",
          group = "OpenStreetMap") %>% # give the layer a name
        leaflet::addProviderTiles(
          provider = "CartoDB.Positron",
          group = "CartoDB.Positron") %>%
        leaflet::addProviderTiles(
          provider = "CartoDB.DarkMatter",
          group = "CartoDB.DarkMatter") %>%
        # add a layers control
        leaflet::addLayersControl(
          baseGroups = c("OpenStreetMap",  "CartoDB.Positron", "CartoDB.DarkMatter"),
          position = "topleft")

      #### Icons and labels;
      colours <- c("red", "orange", "beige", "green", "blue", "purple",
                   "pink", "cadetblue", "gray")

      icons <- leaflet::awesomeIcons(
        icon = "medkit",
        iconColor = "white",
        library = "ion",
        markerColor =
          colours[match(
            .data_$attributes.amenity,
            levels(as.factor(.data_$attributes.amenity)))] %>%
          {ifelse(is.na(.), "black", .)})

      labels <- sprintf(
        "<strong> %s </strong> <br/> %s",
        .data_$attributes.name,
        .data_$attributes.amenity) %>%
        purrr::map(htmltools::HTML)

      makeLegend = function(map, colors, labels, shapes  = "square",
                            sizes = 30,
                            borders ="black"){
        legend_colors = paste0(colors, "; width:", sizes, "px; height:",
                               sizes, "px; border:3px solid ", borders,
                               "; border-radius:", shapes)
        legend_labels = paste0("<div style='display: inline-block;height: ",
                               sizes, "px;margin-top: 4px;line-height: ",
                               sizes, "px;'>", labels, "</div>")

        return(leaflet::addLegend(map, colors = legend_colors,
                                  labels = legend_labels, group = 'Legend'))
      }

      map <- basemap %>%
        leaflet::addAwesomeMarkers(
          lng = ~ lng, lat = ~ lat,
          icon = icons,
          label = ~ labels,
          clusterOptions = leaflet::markerClusterOptions())

      return(map)
    }
  )
)
dark-peak-analytics/who_decide_AP documentation built on May 25, 2022, 8:31 p.m.