lvalnegri icon

global.R

lvalnegri | PRO | 05/16/18 11:18:36 PM UTC | 0 ⭐ | 474 👁️ | Never ⏰ | []
R |

49.31 KB

|

None

|

0 👍

/

0 👎

###################################
# SHINY Explorer - global.R
###################################
 
#===== LOAD PACKAGES -----------------------------------------------------------------------------------------------------------
pkg <- c(
     'bsplus', 'Cairo', 'colourpicker', 'data.table', 'DT', 'dygraphs', 'fst', 'ggplot2', 'ggiraph', 'ggrepel', 'ggthemes',
    'htmltools', 'leaflet', 'mapview', 'plyr', 'RColorBrewer', 'rgdal', 'RMySQL', 'rpivotTable', 'rvest', 'scales', 
    'shiny', 'shinycssloaders', 'shinyDND', 'shinyjs', 'shinyjqui', 'shinythemes', 'shinyWidgets', 
    'sp', 'tidyr', 'xts'
#    'circlize', 'extrafont', 'GGally', 'ggmap', 'ggspatial', 'ggparallel', 'tmap', 
)
invisible( lapply(pkg, require, character.only = TRUE) )
# for(conn in dbListConnections(MySQL())) dbDisconnect(conn)
 
#===== GENERAL OPTIONS ----------------------------------------------------------------------------------------------------------
options(spinner.color = '#e5001a', spinner.size = 1, spinner.type = 4)
 
#===== LOAD DATA ----------------------------------------------------------------------------------------------------------------
# From fst shared rep
dataset <- read.fst(paste0(data.path, audit, '_dt.fst'), as.data.table = TRUE )
# The following recoding is necessary until a new version of the fst package fixes the bug with dates converted into integers
dataset[, `:=`(
    date.day = as.Date(date.day, origin = '1970-01-01'),
    date.week = as.Date(date.week, origin = '1970-01-01'),
    date.month = as.Date(date.month, origin = '1970-01-01'),
    date.quarter = as.Date(date.quarter, origin = '1970-01-01')
)] 
if(has_consultants){
    dt_cons <- read.fst(paste0(data.path, audit, '_pc.fst'), as.data.table = TRUE )
    consultants <- read.fst(paste0(data.path, 'consultants.fst'), as.data.table = TRUE )
}
# From databases
db_conn <- dbConnect(MySQL(), group = 'shiny', dbname = audit)
# dataset <- suppressWarnings(data.table(dbReadTable(db_conn, 'shinyexp_dataset'), key = 'HSP_id' ) )
lookups <- suppressWarnings(data.table(dbGetQuery(db_conn, 'SELECT * FROM lookups WHERE domain_id != 9') ) )
vars <- suppressWarnings(data.table(dbReadTable(db_conn, 'vars') ) )
metrics <- suppressWarnings(data.table(dbReadTable(db_conn, 'metrics') ) )
calendar <- suppressWarnings(data.table(dbReadTable(db_conn, 'calendar') ) )
load.tabs <- suppressWarnings(data.table(dbReadTable(db_conn, 'tabs'), key = 'name' ) )
# dt_cons <- suppressWarnings(data.table(dbReadTable(db_conn, 'procedures_consultants'), key = 'proc_id' ) ) 
completeness <- suppressWarnings(data.table(dbGetQuery(db_conn, 'SELECT item, filter_by FROM completeness WHERE is_active') ) ) 
dbDisconnect(db_conn)
db_conn <- dbConnect(MySQL(), group = 'shiny', dbname = 'common')
areas <- suppressWarnings(data.table(dbReadTable(db_conn, 'locations') ) )
centres <- suppressWarnings(data.table(dbReadTable(db_conn, 'centres'), key = 'HSP_id') )
font.table <- suppressWarnings(data.table(dbGetQuery(db_conn, 'SELECT DISTINCT family FROM fonts WHERE is_active ORDER BY family') ) )
maptiles <- suppressWarnings(data.table(dbGetQuery(db_conn, 
                    'SELECT CONCAT(provider, ".", name) AS name, url, attribution FROM maptiles WHERE require_reg = 0 ORDER BY name'
)))
# consultants <- suppressWarnings(data.table(dbReadTable(db_conn, 'consultants'), key = 'consultant_id' ) ) 
dbDisconnect(db_conn)
hospitals <- centres[get(audit) == 1]
 
 
#===== FUNCTIONS ----------------------------------------------------------------------------------------------------------------
# ifelse(x1, x2, NA)
ifna <- function(x1, x2) ifelse(x1, x2, NA)
# Create a list of variables from <vars> specifying one or more "type" (type could be: LGC, CAT, NUM, GEO, TMS)
build_uiV <- function(tp){
    ui.list <- as.list(vars[nature %in% tp & is_active == 1][order(ordering)][, lookup_id])
    names(ui.list) <- vars[nature %in% tp & is_active == 1][order(ordering)][, description]
    return(ui.list)
}
# Create a list of reference variables from <metrics> specifying a "subtab" and a (optional) type
build_uiY <- function(tb, tp = 1:4){
    ui.list <- as.list(metrics[get(tb) == 1 & type %in% tp ][order(ordering)][, label])
    return(ui.list)
}
# Create a list of locations from <hospitals> specifying a "hierarchy"
build_uiG <- function(h){
    y <- unique(hospitals[, .( get(h), get(paste0(h, '_id')) )][!(V1 %in% labels.del)][order(V1)])
    ui.list <- as.list(y[, V2])
    names(ui.list) <- y[, V1]
    return(ui.list)
}
# Create a list of values from <lookups> specifying a variable
build_uiF <- function(v){
    y <- lookups[domain_id == v, .(lookup_id, description ) ][order(lookup_id)][!(description %in% labels.del)]
    ui.list <- as.list(y[, lookup_id])
    names(ui.list) <- y[, description]
    return(ui.list)
}
# Create a combobox of locations for a given tab 'tb', specifying child 'lw' and parent 'lb' area codes, and a parent area id 'ipt'
build_uiGeo <- function(tb, hg, lw, ipt){
    hg.id <- paste(hg, '_id', sep = '')
    lw.id <- paste(lw, '_id', sep = '')
    inp.obj <- paste('cbo_', tb, '_', hg, sep = '') 
    ui.list <- c('TOTAL')
    if(ipt != 'TOTAL'){
        ui.list <- as.list(unique(hospitals[get(hg.id) == ipt][order(get(lw))][, get(lw.id)]))
        names(ui.list) <- unique(hospitals[get(hg.id) == ipt][order(get(lw))][, get(lw)])
    }
    if(length(ui.list) > 1) ui.list <- c('TOTAL', ui.list)
    return(ui.list)
}
# Build the title for most substabs
build.title <- function(var.Y, is.Y.ref = TRUE, var.X, var.X2 = NA, fnl.area = NA, has.pct = FALSE, var.G1, var.G2 = 'NONE', var.F, val.F){
    ttl <- 
        if(is.Y.ref){
            paste( metrics[label == var.Y, title], ifelse(has.pct, '(%)', ''))
        } else {
            clear.label(var.Y)
        }
    ttl <- paste( ttl, 'by', var.X )
    if(!is.na(var.X2)) ttl <-  paste(ttl, 'and', var.X2)
    if(!is.na(fnl.area)) ttl <-  paste(ttl, 'in', fnl.area)
    if(var.G1 != 'NONE')
        ttl <-  paste0(
                    ttl, '<br/>', 
                    'Grouped by ', clear.label(var.G1), if(var.G2 != 'NONE'){ paste(' and', clear.label(var.G2)) } 
                )
    if(var.F != 'NONE'){
        if(val.F != 'NONE')
            ttl <-  paste0(
                        ttl, '<br/>', 
                        'Filtered by ', clear.label(var.F), ' = <i>', lookups[domain_id == gsub('X', '', var.F) & lookup_id == val.F, description], '</i>' 
            )
    }
    trimws(gsub('  ', ' ', ttl))
}
# Return filter and function for a metric, according to "normal" or "percentage" behaviour
mtc.parsed <- function(mtc, pct = FALSE){
    if(!pct){
        F <- metrics[label == eval(mtc), filter_by]
        M <- metrics[label == eval(mtc), mutate_as]
    } else {
        F <- metrics[label == eval(mtc), filter_pct]
        M <- metrics[label == eval(mtc), mutate_pct]
    }
    return( parse(text = c(F, M) ) )
}
# Given a categorical var id (without the leading "X") and one or some of its ids, return the corresponding label(s)
get.Xlabel <- function(x.id, x.val = 1){
    x.id <- as.numeric(gsub('X', '', x.id))
    lookups[domain_id == x.id & lookup_id %in% x.val, description]
}
# Renames the dataset for pivot table 
mydt.rename <- function(dt){
    nm <- names(dt)[substr(names(dt), 1, 1) == 'X']
    nm <- c('HSP_id', 'HSP', 'CCG', 'LAT', 'NHSR', 'CCR', 'CTRY', paste('datec.', c('day', 'week', 'month', 'quarter', 'year'), sep = ''), nm)
    dt <- dt[, nm, with = FALSE]
    m1 <- as.data.table(names(dt), key = 'V1')
    m2 <- data.table(lookups[domain_id <= 1, .( old = paste('X', lookup_id, sep = ''), new = description ) ], key = 'old')
    names(dt) <- m2[m1][is.na(new), new := old][, new]
    setnames(dt,
        c('HSP_id','HSP', 'CCG', 'LAT', 'NHSR', 'CCR', 'CTRY', 'datec.day', 'datec.week', 'datec.month', 'datec.quarter', 'datec.year'),
        c('Hospital_code','Hospital', 'Comm. Group', 'Area Team', 'NHS Region', 'Comm. Region', 'Country', 'day', 'week', 'month', 'quarter', 'year')
    )
    dt
}
# Returns a renamed dataset after specifying columns and optionally records to retain
get.dt.renamed <- function(columns, records = NA){
    columns <- c('HSP_id', 'HSP', 'datec.day', columns[substr(columns, 1, 1) == 'X'])
    if(is.na(records)){
        dt <- dataset[, columns, with = FALSE]
    } else {
        dt <- dataset[HSP_id %in% records, columns, with = FALSE]
    }
    m1 <- as.data.table(names(dt), key = 'V1')
    m2 <- data.table(lookups[domain_id <= 1, .( old = paste('X', lookup_id, sep = ''), new = description ) ], key = 'old')
    names(dt) <- m2[m1][is.na(new), new := old][, new]
    dt[, Hospital := paste(HSP_id, '-', HSP)]
    dt[, `:=`(HSP_id = NULL, HSP = NULL)]
    setnames(dt, 'datec.day', 'day')
    setcolorder(dt, c('Hospital', setdiff(names(dt), 'Hospital')))
    dt[order(Hospital, -day)]
}
# Renames the exporting dataset for easier reading 
dt.for.export <- function(dt){
    fld2save <- c('date.day', 'date.hour', 'datec.month', 'datec.quarter', 'date.year', 'HSP_id', 'HSP', 'CCG', 'LAT', 'NHSR', 'CCR', 'CTRY')
    fld2name <- c('day', 'hour', 'month', 'quarter', 'year', 'hospital_code', 'hospital', 'Comm. Group', 'Area Team', 'NHS Region', 'Comm. Region', 'Country')
    for(idx in 1:length(names(dt))){
        if(substr(names(dt)[idx], 1, 1) == 'X'){
            fld2save <- c(fld2save, names(dt)[idx])
            fld2name <- c(fld2name, lookups[lookup_id == substr(names(dt)[idx], 2, nchar(names(dt)[idx])), description ])
        }
    }
    dt <- dt[, fld2save, with = FALSE]
    setnames(dt, fld2save, fld2name)
    dt
}
# Clean the filename, keep only alphanum, dash and underline
filename.clean <- function(fn){
    fn <- gsub('[ \\.]', '-', fn)
    gsub('[^[:alnum:]_-]', '', fn)
}
# Clean the title of the plot for exporting
plot.title.clean <- function(fn){
    fn <- gsub('<br/>', '\n', fn)
    fn <- gsub('<(.*)>', '', fn)
    fn
}
# Trim a dataset based on the values of (usually) a slider input wrt some quantity
shrink.dataset <- function(dt, field.from, quant.obj){
    setnames(dt, field.from, 'quantity')
    limInfQuant <- as.numeric(quant.obj)[1]/100
    limSupQuant <- as.numeric(quant.obj)[2]/100
    if(limInfQuant > 0 | limSupQuant < 1)
    dt <- dt[ quantity >= quantile(quantity, limInfQuant, na.rm = TRUE) & quantity <= quantile(quantity, limSupQuant, na.rm = TRUE) ]
    setnames(dt, 'quantity', field.from)
    return(dt)
}
# delete number of item in questionaire from label
clear.label <- function(lbl){
    lbl <- vars[lookup_id == eval(lbl), description]
    if(grepl('^[[:digit:]]', lbl)) return( substr(lbl, regexpr(' ', lbl) + 1, nchar(lbl)) )
    lbl
}
# return the correct expression for the ggplot mapping argument
get.list.aes <- function(lst.aes) {
    if(!length(lst.aes)) return(NULL)
    result <- 'aes('
    for(idx in 1:length(lst.aes)) result <- paste0(result, names(lst.aes[idx]), ' = ', lst.aes[[idx]], ', ')
    result <- paste0(substr(result, 1, nchar(result) - 2), ')')
    eval(parse(text = result))
}
# convert a ggplot into its corresponding interactive plot from ggiraph extension
gg.to.ggiraph <- function(p, sel.type = 'single', gg.width = 0.8){
        ggiraph( code = {print(p)}, 
            width  = gg.width,
            zoom_max  = 1,
            selection_type = sel.type,
            # selected_css = "",
            tooltip_offx = 20, tooltip_offy = -10,
            hover_css = "fill:red;cursor:pointer;r:4pt;opacity-value:0.5;",
            tooltip_extra_css= "background-color:wheat;color:gray20;border-radius:10px;padding:3pt;",
            tooltip_opacity = 0.9,
            pointsize = 12
        )
}
# calculate both funnel plot limits based on given "total" probability and maximum effect size
get.funnel.limits <- function(plevel, maxn, theta1, theta2 = NULL){
    ord.magn <- ifelse(nchar(maxn) <= 4, 1, nchar(maxn) - 3)
    my.funnel <- data.table(x = seq(1, pretty(maxn)[2], 10^(ord.magn - 1) ) )
    plevel.inf <- (1 - plevel)/2
    plevel.sup <- plevel + plevel.inf
    if(!is.null(theta2)){
        my.funnel[, liminf := qnorm(plevel.inf, theta1, theta2/sqrt(x)) ]
        my.funnel[, limsup := qnorm(plevel.sup, theta1, theta2/sqrt(x)) ]
    } else {
        my.funnel[, nu := qbinom(plevel.inf, x, theta1) ]
        my.funnel[, num := ( pbinom(nu, x, theta1) - plevel.inf ) ]
        my.funnel[, den := ( pbinom(nu, x, theta1) - pbinom(nu - 1, x, theta1) ) ]
        my.funnel[, alpha := num / den ]
        my.funnel[, liminf := (nu - alpha) / x ]
        my.funnel[, nu := qbinom(plevel.sup, x, theta1) ]
        my.funnel[, num := ( pbinom(nu, x, theta1) - plevel.sup ) ]
        my.funnel[, den := ( pbinom(nu, x, theta1) - pbinom(nu - 1, x, theta1) ) ]
        my.funnel[, alpha := num / den ]
        my.funnel[, limsup := (nu - alpha) / x ]
    }
    return(my.funnel[, .(x, liminf, limsup) ])
}
# recode multiple fields as ordered factors, using themselves or the lookup table as reference for the labels/levels
recode.factors <-  function(dt, flds, lkps = FALSE){
    for(fn in flds){
        if(lkps){
            lk <- sub('X', '', fn)
            lvls <- lookups[domain_id == lk, lookup_id ]
            lbls <- lookups[domain_id == lk, description ]
        } else {
            lvls <- sort(unique(dt[[fn]]))
            lvls <- lvls[!(lvls %in% c(NA))]
            lbls <- lvls
        }
        dt[, (fn) := factor(dt[[fn]], levels = lvls, labels = lbls, ordered = TRUE) ]
    }
}
# get long name of day/month starting from 3-chars name
get.longname <- function(shortname){
    longname <- lookups[domain_id == 200091 & lookup_id == lookups[domain_id == 200092 & description == shortname, lookup_id], description]
    if(length(longname) > 0) return(longname)
    longname <- lookups[domain_id == 200093 & lookup_id == lookups[domain_id == 200094 & description == shortname, lookup_id], description]
    if(length(longname) > 0) return(longname)
    shortname
}
vget.longname <- Vectorize(get.longname)
# get the summary dataset for the different tabs V1
get.y.tms <- function(dt, var.Y, var.X = NA, grp1 = NA, grp2 = NA, tt = NULL, pct = FALSE, flt.var = NA, flt.val = NA){
    # retrieve formulas for calculate the metric  
    mtc.def <- mtc.parsed(var.Y, pct)
    # apply the filter that define the metric
    y <- dt[ eval(mtc.def[1]) ]
    # if needed, determine the independent var X
    if(!is.na(var.X)) y[, X := get(var.X) ][!is.na(X)]
    # if needed, filter over the specified variable
    if(!is.na(flt.var)){
        if(flt.val != 'NONE'){
            flt.lbl <- lookups[domain_id == gsub('X', '', flt.var) & lookup_id == flt.val, description]
            y <- y[ get(flt.var) == flt.lbl ]
        }
    }
    # calculate the metrics
    if(is.na(grp1)){
        # ungrouped case
        y <- y[, .( Y = eval(mtc.def[2]) ), X ][, G := 1]
    } else {
       if(is.na(grp2)){
            # grouped case, one grouping var
            y <- y[, G := get(grp1) ][!(G %in% labels.del)][, .( Y = eval(mtc.def[2]) ), .(X, G) ]
        } else {
            # grouped case, two grouping vars
            y <- y[, G1 := get(grp1) ][!(G1 %in% labels.del)][, G2 := get(grp2) ][!(G2 %in% labels.del)]
            y <- y[, .( Y = eval(mtc.def[2]) ), .(X, G1, G2) ][, G := 1]
        }
    }
    # if needed, calculate the tooltip
    if(!is.null(tt))
        y[, ttip := paste0(
                        if(!is.na(tt[1])){ paste0('<b>', tt[1], '</b><br/>') },
                        tt[2], ': <b>', X, '</b><br/>',
                        if(!is.na(grp1)){ 
                            if(is.na(grp2)){ 
                                paste0(clear.label(grp1), ': <b>', G, '</b><br/>')
                            } else {
                                paste0(clear.label(grp1), ': <b>', G1, '</b><br/>', clear.label(grp2), ': <b>', G2, '</b><br/>') 
                            }
                        },
                        metrics[label == var.Y, title], ': <b>', if(pct) { paste0(100*Y, '%') } else {Y}, '</b>'
        )]
    return(y)
}
# return the summary dataset to display in the "table" subtab
get.dt.tbl <- function(dt, 
                tblY, lblX, fld.to.order = NA, ord.desc = FALSE, is.time = FALSE,
                col.bars = '#80cdc1', col.fonts = 'black', pal.scale = 'Spectrum', n.cols = 7, reverse = FALSE,
                flt.var = NA, flt.val = NA, grp.var = NA, grp.type = NA, grp.stype = NA
    ){
    ## A1- DETERMINE THE MAIN DATASET -----------------------------------------------------------------------
    # Tables display following columns: 
    #  - type =  1 (quantities) : 'Count', 'Quota'
    #  - type =  2 (measure)    : 'Count', 'Quota', 'Percent', 'Index'
    #  - type >= 3 (metrics)    : 'Value', 'Index'
        # filtering
        if(!is.na(flt.var)){
            if(!is.na(flt.val)){
                flt.lbl <- lookups[domain_id == gsub('X', '', flt.var) & lookup_id == flt.val, description]
                dt <- dt[ get(flt.var) == flt.lbl ]
            }
        }
        # query the type of the variable
        var.type <- min(3, metrics[label == tblY, type])    
        # query filter and formula for counting  (type = 1) and metrics (type >= 3)
        YF <- metrics[label == tblY, filter_by]
        YM <- metrics[label == tblY, mutate_as]
        # filter the dataset for the main table
        y <- dt[eval( parse(text = YF) )]
        # calculate and store total if metric
        if(var.type == 3) yt <- y[, eval( parse(text = YM) ) ]
        # calculate counting or metric
        if(is.time){
            y <- y[ , .( m = eval( parse(text = YM) ) ), X ][order(-X)]
        } else {
            y <- y[ , .( m = eval( parse(text = YM) ) ), .( X = as.character(X) ) ]
        }
        if(var.type == 3){
            # calculate Index if metric
            y[, yt := yt][, Index := round(m / yt, 4)][, yt := NULL]
        } else {
            # calculate and store total if counting
            yt <- y[, sum(m)]
            y[, Quota := round(m / sum(m), 4)]
        }
        # define row for the total
        df.total <- data.frame('TOTAL', yt, NA)
        if(var.type == 2){
            # calculataion percentages as above if measure (type = 2)
            YFP <- metrics[label == tblY, filter_pct]
            YMP <- metrics[label == tblY, mutate_pct]
            yp <- dt[eval( parse(text = YFP) )]
            ytp <- yp[, eval( parse(text = YMP) ) ]
            yp <- yp[ , .( m = eval( parse(text = YMP) ) ), .( X = as.character(X) ) ]
            yp[, ytp := ytp][, Index := round(m / ytp, 4)][, ytp := NULL]
            setkey(y, 'X')
            setkey(yp, 'X')
            y <- y[yp]
            df.total <- data.frame('TOTAL', yt, NA, ytp, NA)
        }
        # order table according to user choices: variable + type
        setnames(y, c(lblX, tbl.headings[[var.type]]))
        if(!is.na(fld.to.order)) y <- y[order(get(fld.to.order), decreasing = ord.desc)]
        # add total row at the top
        y <- rbindlist(list( df.total, y) )
        # rename columns
        setnames(y, c(lblX, tbl.headings[[var.type]]))
 
    ## A2- IF REQUESTED, DETERMINE AND ADD TO THE ABOVE THE GROUP DATASET -----------------------------------
    # Tables display following columns: 
    #  - type = 1 (quantities):
    #    - 'Count'
    #    - 'Quota': vs Total National, vs Grouped Item
    #  - type =  2 (measure)    : 'Count', 'Quota', 'Percent', 'Index'
    #    - 'Count'
    #    - 'Quota': vs Total National, vs Grouped Item
    #    - 'Percentage': vs Total National, vs Grouped Item
    #    - 'Index' (vs Grouped Items only)
    #  - type >= 3 (metrics)'', ''
    #    - 'Value'
    #    - 'Index' (vs Grouped Items only)
        if(!is.na(grp.var)){
            dt[, G := get(grp.var) ]
            yg <- dt[!(G %in% labels.del)][eval( parse(text = YF) )]
            if(var.type == 1){
                yg <- yg[, .( m = eval( parse(text = YM) ) ), .( X = as.character(X), G ) ]
                yg <- dcast.data.table(yg, X~G, value.var = 'm', fill = 0)
                yn <- names(yg)
                yg <- rbindlist( list( data.frame('TOTAL', yg[, lapply(.SD, sum), .SDcols = 2:ncol(yg)]), yg) )
                names(yg) <- yn
                y <- merge(y, yg, by.x = lblX, by.y = 'X')
                if(grp.type == 'Quota'){
                    y[, T := rowSums(y[,4:ncol(y), with = FALSE])]
                    yq <- 
                        if(grp.stype == 'R'){
                            y[, lapply(.SD, function(x) round(x/T, 4)), .SDcols = 4:ncol(y)]
                        } else if(grp.stype == 'C'){
                            y[, lapply(.SD, function(x) round(x/x[1], 4)), .SDcols = 4:ncol(y)]
                            
                        } else {
                            y[, lapply(.SD, function(x) round(x/sum(T[2:.N]), 4)), .SDcols = 4:ncol(y)]
                        }
                    yq[, T := NULL]
                    if(grp.stype == 'C') yq[1, names(yq) := NA]
                    y <- cbind(y[, 1:3], yq)
                }
            } else if(var.type == 2){
            
            } else {
                ygt <- t(yg[, .( m = eval( parse(text = YM) ) ), G ][, m])
                yg <- yg[, .( m = eval( parse(text = YM) ) ), .( X = as.character(X), G ) ]
                yg <- dcast.data.table(yg, X~G, value.var = 'm', fill = NA)
                yn <- names(yg)
                yg <- rbindlist( list( data.frame('TOTAL', ygt), yg) )
                names(yg) <- yn
                y <- merge(y, yg, by.x = lblX, by.y = 'X')
                if(grp.type == 'Index'){
                    if(grp.stype == 'R'){
                        yq <- y[, lapply(.SD, function(x) round(x/Value, 4)), .SDcols = 4:ncol(y)]
                    } else if(grp.stype == 'C'){
                        yq <- y[, lapply(.SD, function(x) round(x/x[1], 4)), .SDcols = 4:ncol(y)]
                        yq[1, names(yq) := NA]
                    }
                    y <- cbind(y[, 1:3], yq)
                }
            }
        }
        
    ## B- BUILD THE TABLE -----------------------------------------------------------------------------------
        t <- datatable(y,
                rownames = FALSE,
                selection = 'none',
                class = 'cell-border stripe hover nowrap',
                extensions = c('Buttons', 'FixedColumns', 'Scroller'),
                options = list(
                    scrollX = TRUE,
                    scrollY = 400,
                    scroller = TRUE,
                    buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
                    fixedColumns = list(leftColumns = 1),
                    ordering = FALSE,
                    deferRender = TRUE,
                    dom = 'Btip'
                )
        )
 
    ## C- STYLE THE DATATABLE -------------------------------------------------------------------------------
        if('Counting' %in% names(y)){
            t <- t %>% formatCurrency('Counting', '', digits = 0)
        }
        if('Quota' %in% names(y)){
            t <- t %>% formatPercentage('Quota', digits = 2)
            t <- t %>% formatStyle('Quota',
                            color = col.fonts,
                            background = styleColorBar(y[, Quota], col.bars ),
                            backgroundSize = '90% 70%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center'
            )
        }
        if('Percentage' %in% names(y)){
            t <- t %>% formatPercentage('Percentage', digits = 2)
            t <- t %>% formatStyle('Percentage',
                            color = col.fonts,
                            background = styleColorBar(y[, Percentage], col.bars ),
                            backgroundSize = '90% 70%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center'
            )
        }
        if('Value' %in% names(y)){
            t <- t %>% formatCurrency('Value', '', digits = 2)
            t <- t %>% formatStyle('Value',
                            color = col.fonts,
                            background = styleColorBar(y[, Value], col.bars ),
                            backgroundSize = '90% 70%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center'
            )
        }
        if('Index' %in% names(y)){
            t <- t %>% formatCurrency('Index', '', digits = 3)
            pal <- brewer.pal(n.cols + 1, pal.scale)
            if(reverse) pal <- rev(pal)
            t <- t %>% formatStyle('Index', 
                            backgroundColor = styleInterval(
                                quantile(y$Index, prob = seq(0, 1, length = n.cols + 1), na.rm = TRUE)[2:(n.cols + 1)],
                                pal
                            )
            )
        }
        if(!is.na(grp.var)){
            tg.names <- names(y[, 4:ncol(y)])
            tg.range <- y[2:nrow(y), 4:ncol(y)]
            if(var.type == 1){
                if(grp.type == 'Counting'){
                    t <- t %>% formatCurrency(tg.names, '', digits = 0)
                    # if(grp.stype == 'T'){
                    #     t <- t %>% formatStyle(tg.names,
                    #                   background = styleColorBar(as.matrix(tg.range), 'lightblue'),
                    #                   backgroundSize = '90% 80%',
                    #                   backgroundRepeat = 'no-repeat',
                    #                   backgroundPosition = 'center'
                    #            )
                    # }
                } else {
                    t <- t %>% formatPercentage(tg.names, digits = 2)
                }
            } else if(var.type == 2){
                
            } else {
                t <- t %>% formatCurrency(tg.names, '', digits = 2 + as.numeric(grp.type == 'Index'))
            }
        }
        # format TOTAL first row as bigger font size, bold typeface.
        t <- t %>% formatStyle(lblX, target = 'row', 
                            backgroundColor = styleEqual('TOTAL', 'black' ),
                            color = styleEqual('TOTAL', 'white' ),
                            fontWeight = styleEqual('TOTAL', 'bold'), 
                            fontSize = styleEqual('TOTAL', '130%' )
        )
 
    ## RETURN -----------------------------------------------------------------------------------------------
        t
}
# returns the summary dataset for the BARPLOT as a 2-elements list: 1st the dataset, 2nd the national total
get.dt.brp <- function(dt, var.Y, var.X = NA, grp1 = NA, grp2 = NA, tt = NULL, pct = FALSE, flt.var = NA, flt.val = NA, show.NA = FALSE, ordering = 1){
    # retrieve formulas for calculate the metric  
    mtc.def <- mtc.parsed(var.Y, pct)
    # apply the filter that define the metric
    y <- dt[ eval(mtc.def[1]) ]
    # if needed, determine the independent var X
    if(!is.na(var.X)) y[, X := get(var.X)][!is.na(X)]
#    y[, X := as.character(X)]
    # if needed, filter over the specified variable
    if(!is.na(flt.var)){
        if(flt.val != 'NONE'){
            flt.lbl <- lookups[domain_id == gsub('X', '', flt.var) & lookup_id == flt.val, description]
            y <- y[ get(flt.var) == flt.lbl ]
        }
    }
    # calculate the metrics
    if(is.na(grp1)){
        # ungrouped case
        yt <- as.numeric(y[, .( Y = eval(mtc.def[2]) ) ])
        y <- y[, .( Y = eval(mtc.def[2]) ), X ][, G := 1]
    } else {
       if(is.na(grp2)){
            # grouped case, one grouping var
            y <- y[, G := get(grp1) ][!(G %in% labels.del)]
            yt <- y[, .( Y = eval(mtc.def[2]) ), G ]
            y <- y[, .( Y = eval(mtc.def[2]) ), .(X, G) ]
            if(show.NA)
                y <- setDT(complete(y, X, nesting(G), fill = list(Y = 0) ) )
        } else {
            # grouped case, two grouping vars
            y <- y[, G1 := get(grp1) ][!(G1 %in% labels.del)][, G2 := get(grp2) ][!(G2 %in% labels.del)]
            yt <- y[, .( Y = eval(mtc.def[2]) ), .(G1, G2) ]
            y <- y[, .( Y = eval(mtc.def[2]) ), .(X, G1, G2) ][, G := 1]
        }
    }
    # if needed, calculate the tooltip
    if(!is.null(tt))
        y[, ttip := paste0(
                        if(!is.na(tt[1])){ paste0('<b>', tt[1], '</b><br/>') },
                        tt[2], ': <b>', X, '</b><br/>',
                        if(!is.na(grp1)){ 
                            if(is.na(grp2)){ 
                                paste0(clear.label(grp1), ': <b>', G, '</b><br/>')
                            } else {
                                paste0(clear.label(grp1), ': <b>', G1, '</b><br/>', clear.label(grp2), ': <b>', G2, '</b><br/>') 
                            }
                        },
                        metrics[label == var.Y, title], ': <b>', if(pct) { paste0(100*Y, '%') } else {Y}, '</b>'
        )]
    # cancel factor on X
    y[, X := as.character(X)]
    # order bars
    y <- switch(ordering, '1' = y[order(X)], '2' = y[order(-X)], '3' = y[order(Y)], '4' = y[order(-Y)] )
    
    return( list(y, yt, ytg = NA, ytg2 = NA) )
}
# returns the summary dataset for the BOXPLOT as a 2-elements list: 1st the dataset, 2nd the outliers
get.dt.bxp <- function(dt, var.Y, var.X = NA, grp1 = NA, grp2 = NA, tt = NULL, flt.var = NA, flt.val = NA){
    cols = c('X')
    y0 <- dt[, Y := get(var.Y) ]
    if(!is.na(var.X)) y0[, X := get(var.X)][!is.na(X)]
    if(!is.na(grp1)){
        y0 <- y0[, G := get(grp1) ][!(G %in% labels.del)]
        cols = c(cols, 'G')
        if(!is.na(grp2)){
            y0 <- y0[, G2 := get(grp2) ][!(G2 %in% labels.del)]
            cols = c(cols, 'G2')
        }
            
    }
    # if needed, filter over the specified variable
    if(!is.na(flt.var)){
        if(flt.val != 'NONE'){
            flt.lbl <- lookups[domain_id == gsub('X', '', flt.var) & lookup_id == flt.val, description]
            y0 <- y0[ get(flt.var) == flt.lbl ]
        }
    }
    y <- y0[, .SD, .SDcols = c(cols, 'Y')]
    setkeyv(y, cols)
    yt <- y[, .(V0 = sum(is.na(Y))), cols]
    y <- y[!(Y %in% labels.del)]
    yt <- yt[y[, .(
                    V1 = .N, V2 = median(Y), V3 = quantile(Y)[2], V4 = quantile(Y)[4], V5 = IQR(Y), V6 = paste(range(Y), collapse = '-'), 
                    V11 = round(mean(Y), 2), V12 = round(sd(Y), 2) 
                  ),
                  cols 
    ]]
    yt <- yt[y[, .(V7 = sum( Y < (quantile(Y)[2] - IQR(Y) * 1.5) | Y > (quantile(Y)[4] + IQR(Y) * 1.5) )), cols ]]
    yt[, ttip := as.factor(paste0(
            tt, ': <b>', X, '</b><br/>',
            if(!is.na(grp1)) paste0(clear.label(grp1), ': <b>', G, '</b><br/>'),
            if(!is.na(grp2)) paste0(clear.label(grp2), ': <b>', G2, '</b><br/>'),
            'N. Procedures: <b>', format(V1, big.mark = ','), '</b><br/><br/>',
            toupper(clear.label(var.Y)), '<ul>',
            '<li>Completeness: <b>', ifelse(V0 > 0, paste0('<font color="red">', paste0(round(100*(1-V0/(V0+V1)), 2), '%'), '</font>'), '100%'), '</b><br/>',
            '<li>Median: <b>', V2, '</b><br/>',
            '<li>1st quartile: <b>', V3, '</b><br/>',
            '<li>3rd quartile: <b>', V4, '</b><br/>',
            '<li>IQR: <b>', V5, '</b><br/>',
            '<li>Range: <b>', V6, '</b><br/>',
            '<li>N. Outliers: <b>', ifelse(V7 > 0, paste0('<font color="red">', V7, '</font>'), '--'), '</b><br/>',
            '<li>Mean: <b>', V11, '</b><br/>',
            '<li>St Dev: <b>', V12, '</b><br/></ul>'
    ))]
    y <- y[yt[, c(cols, 'ttip'), with = FALSE]]
    # outliers        
    y0[, D := datec.day]
    y.out <- y0[!(Y %in% labels.del)][Y < (quantile(Y)[2] - IQR(Y) * 1.5) | Y > (quantile(Y)[4] + IQR(Y) * 1.5)][, c(cols, 'D', 'Y'), with = FALSE]
    y.out[, ttip := paste0(
                tt, ': <b>', X, '</b><br/>',
                if(!is.na(grp1)) paste0(clear.label(grp1), ': <b>', G, '</b><br/>'),
                if(!is.na(grp2)) paste0(clear.label(grp2), ': <b>', G2, '</b><br/>'),
                clear.label(var.Y), ': ', Y, '<br/>',
                'Date of Procedure: ', D
    )]
    # cancel factor on X
    y[, X := as.character(X)]
    
    return( list(y, y.out) )
}
# returns the summary dataset for the HEATMAPS
get.dt.hmp <- function(dt, var.Y, var.X = NA, var.X2 = NA, grp1 = NA, grp2 = NA, tt = NULL, pct = FALSE, flt.var = NA, flt.val = NA, mtc.rescale = NA, ordering = 1, show.NA = FALSE){
    # retrieve formulas for calculate the metric  
    mtc.def <- mtc.parsed(var.Y, pct)
    # apply the filter that define the metric
    y <- dt[ eval(mtc.def[1]) ]
    # if needed, determine the independent vars X and X2
    if(!is.na(var.X)) y[, X := get(var.X)][!is.na(X)]
    if(!is.na(var.X2)) y[, X2 := get(var.X2)][!is.na(X2)]
    # if needed, filter over the specified variable
    if(!is.na(flt.var)){
        if(flt.val != 'NONE'){
            flt.lbl <- lookups[domain_id == gsub('X', '', flt.var) & lookup_id == flt.val, description]
            y <- y[ get(flt.var) == flt.lbl ]
        }
    }
    # calculate the metrics
    if(is.na(grp1)){
        # ungrouped case
        y <- y[, .( Y = eval(mtc.def[2]) ), .(X, X2) ]
        if(show.NA) y <- setDT(complete(y, X, nesting(X2)) )
    } else {
       if(is.na(grp2)){
            # grouped case, one grouping var
            y <- y[, G := get(grp1) ][!(G %in% labels.del)]
            y <- y[, .( Y = eval(mtc.def[2]) ), .(X, X2, G) ]
            if(show.NA) y <- setDT(complete(y, X, nesting(X2, G) ) )
        } else {
            # grouped case, two grouping vars
            y <- y[, G1 := get(grp1) ][!(G1 %in% labels.del)]
            y <- y[, G2 := get(grp2) ][!(G2 %in% labels.del)]
            y <- y[, .( Y = eval(mtc.def[2]) ), .(X, X2, G1, G2) ][, G := 1]
            if(show.NA) y <- setDT(complete(y, X, nesting(X2, G1, G2) ) )
        }
    }
    # calculate the tooltip
    if(length(tt)){
        y[, ttip := paste0(
                        tt[1], ': <b>', X, '</b><br/>',
                        tt[2], ': <b>', X2, '</b><br/>',
                        if(!is.na(grp1)){ 
                            if(is.na(grp2)){ 
                                paste0(clear.label(grp1), ': <b>', G, '</b><br/>')
                            } else {
                                paste0(clear.label(grp1), ': <b>', G1, '</b><br/>', clear.label(grp2), ': <b>', G2, '</b><br/>') 
                            }
                        },
                        metrics[label == var.Y, title], ': <b>', if(pct) { paste0(100*Y, '%') } else {Y}, '</b>'
        )]
    }
    # Rescaling along X-geo or X2-time
    if(!is.na(mtc.rescale)) y <- setDT( ddply(y, .(get(mtc.rescale)), transform, Y = round(rescale(Y) * 100, 0) ) )
    if(ordering){
        # cancel factor on X
        y[, X := as.character(X)]
        # order bars
        y <- switch(ordering, '1' = y[order(X)], '2' = y[order(-X)], '3' = y[order(Y)], '4' = y[order(-Y)] )
    }    
    return( list(y, yt = NA, ytg = NA, ytg2 = NA) )
}
 
# return only the X-items with positions indicated by vals
trim.dt.X <- function(dt, vals){
    if(length(vals) == 0) vals <- c(1, length(unique(dt$X)))
    y <- dt[X %in% unique(dt$X)[vals[1]:vals[2] ] ]
    y$X <- factor(y$X, levels = unique(y$X))
    return(y)
}
# return only the X-items with positions indicated by vals
trim.dt.X.bxp <- function(dt, vals, ordering){
    if(length(vals) == 0) vals <- c(1, length(unique(dt$X)))
    yo <- switch(ordering, 
            '1' = unique(dt[, .(X)])[order(X)][vals[1]:vals[2] ],
            '2' = unique(dt[, .(X)])[order(-X)][vals[1]:vals[2] ],
            '3' = dt[, median(Y), X][order(V1), .(X)][vals[1]:vals[2] ],
            '4' = dt[, median(Y), X][order(-V1), .(X)][vals[1]:vals[2] ]
    )
    yo <- unlist(yo)
    y <- dt[X %in% yo]
    return( list(y, yo) )
}
 
# embellish the dataset to be downloaded from the user as CSV file
dt.csv.output <- function(dt){
    y <- copy(dt)
    y[, `:=`(ttip = NULL, data_id = NULL, clk = NULL)]
}
 
 
 
#===== OPTIONAL TABS ------------------------------------------------------------------------------------------------------------
 
# 
if(load.tabs['consultants', status]){    
    
}
 
# 
if(load.tabs['patients', status]){    
    
}
 
# Build Completeness table, with all different areas and timespans
if(load.tabs['completeness', status]){
    cmp.tbl <- data.table()
    for(itm in 1:nrow(completeness)){
        W = parse(text = paste0(completeness[itm, filter_by]))
        cmp.tbl <- rbindlist(list(
            cmp.tbl,
            cbind(
                item = completeness[itm, item],
                dataset[,
                    .( count = sum( eval(W) ), total = .N ),
                    .(HSP_id, year = date.year, quarter = date.quarter, month = date.month)
                ][order(HSP_id, month)]
            )
        ))
    }
    setkey(cmp.tbl, 'HSP_id')
    setkey(hospitals, 'HSP_id')
    cmp.tbl <- cmp.tbl[hospitals[, .(HSP_id, CCG_id, CTRY_id) ] ]
}
 
 
#===== VARIABLES/LABELS ----------------------------------------------------------------------------------------------------------------
 
# List for the GEOGRAPHY combo box 
locations <- c(
    'Country' = 'CTRY', 'Comm. Region' = 'CCR', 'NHS Region' = 'NHSR',  'Area Team' = 'LAT', 'Comm. Group' = 'CCG', 'Hospital' = 'HSP'
)
# List for the TIME REFERENCE single period in heatmaps hospitals
timeref <- c(
    'Year' = 'datec.year', 'Quarter' = 'datec.quarter', 'Month of Year' = 'month_st', 
    'Day of Month' = 'day_nid', 'Day of Week' = 'day_st', 'Daypart' = 'daypart', 'Hour of Day' = 'date.hour' 
) 
# List for the TIME REFERENCE single period in timeseries
timeper <- c(
    'Year' = 'datec.year', 'Quarter' = 'datec.quarter', 'Month' = 'datec.month', 'Week' = 'datec.week', 'Day' = 'datec.day'
) 
# List for the DATES double combo
date.range <- c(
    start = max(dataset$date.day) - 365*3 - 1, 
    min   = min(dataset$date.day),
    max   = max(dataset$date.day)
)
# Values to delete from labels 
labels.del <- c(
    'Unknown', 'Not Applicable', 'Not measured', 'Not known', 'Not specified', NA, ''
)
# List of Function to apply to numeric measures
fun.measures <- c(
    'Mean' = 'mean', 'Minimum' = 'min', 'Maximum' = 'max', 'Interquartile Range' = 'IQR', 'Standard Deviation' = 'sd'
)
# Default color/palette 
pal.default <- c('col' = 'steelblue3', 'cat' = 'Dark2', 'seq' = 'YlGnBu', 'div' = 'RdBu', 'na' = 'grey62')
# List of palettes to be used with ColourBrewer package:  
lst.palette <- list(
    'SEQUENTIAL' = c( # ordinal data where (usually) low is less important and high is more important
        'Blues' = 'Blues', 'Blue-Green' = 'BuGn', 'Blue-Purple' = 'BuPu', 'Green-Blue' = 'GnBu', 'Greens' = 'Greens', 'Greys' = 'Greys',
        'Oranges' = 'Oranges', 'Orange-Red' = 'OrRd', 'Purple-Blue' = 'PuBu', 'Purple-Blue-Green' = 'PuBuGn', 'Purple-Red' = 'PuRd', 'Purples' = 'Purples',
        'Red-Purple' = 'RdPu', 'Reds' = 'Reds', 'Yellow-Green' = 'YlGn', 'Yellow-Green-Blue' = 'YlGnBu', 'Yellow-Orange-Brown' = 'YlOrBr',
        'Yellow-Orange-Red' = 'YlOrRd'
    ), 
    'DIVERGING' = c(  # ordinal data where both low and high are important (i.e. deviation from some reference "average" point)
        'Brown-Blue-Green' = 'BrBG', 'Pink-Blue-Green' = 'PiYG', 'Purple-Red-Green' = 'PRGn', 'Orange-Purple' = 'PuOr', 'Red-Blue' = 'RdBu', 'Red-Grey' = 'RdGy',
        'Red-Yellow-Blue' = 'RdYlBu', 'Red-Yellow-Green' = 'RdYlGn', 'Spectral' = 'Spectral'
    ),  
    'QUALITATIVE' = c(  # categorical/nominal data where there is no logical order
        'Accent' = 'Accent', 'Dark2' = 'Dark2', 'Paired' = 'Paired', 'Pastel1' = 'Pastel1', 'Pastel2' = 'Pastel2',
        'Set1' = 'Set1', 'Set2' = 'Set2', 'Set3' = 'Set3'
    )
)
# list of labels for download buttons
btndwn.text <- c('Save Dataset as CSV', 'Save Chart as PNG', 'Save Static Map as PNG', 'Save Interactive Map as HTML', 'Save Table as CSV')
# list of options for charts
point.shapes <- c('circle' = 21, 'square' = 22, 'diamond' = 23, 'triangle up' = 24, 'triangle down' = 25)
line.types <- c('dashed', 'dotted', 'solid', 'dotdash', 'longdash', 'twodash')
face.types <- c('plain', 'bold', 'italic', 'bold.italic')
val.lbl.pos <- list(
    'Inside'  = list('Vertical' = c(0.5,  1.5), 'Horizontal' = c( 1.2, 0.2) ),
    'Outside' = list('Vertical' = c(0.4, -0.3), 'Horizontal' = c(-0.2, 0.2) )
)
lbl.format <- function(y, type, is.pct = FALSE){
    if(type == 1){ 
        format(y, big.mark = ',', nsmall = 0)
    } else if(type == 2){ 
        if(is.pct){ 
            paste0(format(round(100 * y, 2), nsmall = 2), '%')
        } else { 
            format(y, big.mark = ',', nsmall = 0)
        }    
    } else {
        format(y, nsmall = 2)
    }
}
# default probabilities and colors for funnel plots limits
funnel.defaults <- list( 
    'prob.tot' = c('90%' = 0.9, '95%' = 0.95, '98.5%' = 0.985, '99%' = 0.99, '99.8%' = 0.998, '99.9%' = 0.999),
    'prob.sel' = c(0.998, 0.95),
    'col.tot' = c('green', 'red', 'blue', 'black', 'cyan', 'magenta', 'yellow', 'gray'),
    'col.sel' = c('green', 'red'),
    'type.tot' = line.types,
    'type.sel' = c('solid', 'dotted')  
)
# table headings
tbl.headings <- list(
    'cnt' = c('Counting', 'Quota'),
    'mtc' = c('Counting', 'Quota', 'Percentage', 'Index'),
    'msr' = c('Value', 'Index')
)
# table ordering variables
tbl.orders <- list(
    'cnt' = c('Counting'),
    'mtc' = c('Counting', 'Percentage'),
    'msr' = c('Value')
)
# plot caption
plt.caption <- paste0('@', year(Sys.Date()), ' ATG')
 
 
#===== STYLES ----------------------------------------------------------------------------------------------------------------
 
# add text at the left of the upper navbar
navbarPageWithText <- function(..., text) {
    navbar <- navbarPage(...)
    textEl <- tags$p(class = "navbar-text", text)
    navbar[[3]][[1]]$children[[1]] <- htmltools::tagAppendChild( navbar[[3]][[1]]$children[[1]], textEl)
    navbar
}
# return correct spacing for axis labels rotation
lbl.plt.rotation = function(angle, position = 'x'){
    positions = list(x = 0, y = 90, top = 180, right = 270)
    rads  = (angle - positions[[ position ]]) * pi / 180
    hjust = 0.5 * (1 - sin(rads))
    vjust = 0.5 * (1 + cos(rads))
    element_text(angle = angle, vjust = vjust, hjust = hjust)
}
# global style for ggplot charts
my.ggtheme <- function(g, 
                    xaxis.draw = FALSE, yaxis.draw = FALSE, axis.draw = FALSE, ticks.draw = FALSE, axis.colour = 'black', axis.size = 0.1,
                    hgrid.draw = FALSE, vgrid.draw = FALSE, grids.colour = 'black', grids.size = 0.1, grids.type = 'dotted',
                    labels.rotation = c(45, 0), labels.rotate = FALSE, 
                    bkg.colour = 'white', font.size = 6, ttl.font.size.mult = 1.2, ttl.face = 'bold',
                    legend.pos = 'bottom', plot.border = FALSE, font.family = 'Arial'
              ){
    g <- g + theme(
                text             = element_text(family = font.family),
                plot.title       = element_text(hjust = 0, size = rel(1.2) ),  # hjust: 0-left, 0.5-center, 1-right
                plot.background  = element_blank(),
                plot.margin      = unit(c(1, 0.5, 0, 0.5), 'lines'),  # space around the plot as in: TOP, RIGHT, BOTTOM, RIGHT
                plot.caption     = element_text(size = 8, face = 'italic'),
                axis.line        = element_blank(),
                axis.ticks       = element_blank(),
                axis.text        = element_text(size = font.size, color = axis.colour),
                axis.text.x      = element_text(angle = labels.rotation[1], hjust = 1), # vjust = 0.5),
                axis.text.y      = element_text(angle = labels.rotation[2]), # , hjust = , vjust = ),
                axis.title       = element_text(size = font.size * (1 + ttl.font.size.mult), face = ttl.face),
                axis.title.x     = element_text(vjust = -0.3), 
                axis.title.y     = element_text(vjust = 0.8, margin = margin(0, 10, 0, 0) ),
                legend.text      = element_text(size = 6),
                legend.title     = element_text(size = 8), 
                legend.title.align = 1,
                legend.position  = legend.pos,
                legend.background = element_blank(), 
                legend.spacing   = unit(0, 'cm'),
#                legend.key = element_blank(), 
                legend.key.size  = unit(0.2, 'cm'),
                legend.key.height = unit(0.4, 'cm'),      
                legend.key.width = unit(1, 'cm'),
                panel.background = element_rect(fill = bkg.colour, colour = bkg.colour), 
                panel.border     = element_blank(),
                panel.grid       = element_blank(),
                panel.spacing.x  = unit(3, 'lines'),
                panel.spacing.y  = unit(2, 'lines'),
                strip.text       = element_text(hjust = 0.5, size = font.size * (1 + ttl.font.size.mult), face = ttl.face),
                strip.background = element_blank()
    )
    if(plot.border) g <- g + theme( panel.border = element_rect(colour = axis.colour, size = axis.size, fill = NA) )
    if(axis.draw){
        g <- g + theme( axis.line = element_line(color = axis.colour, size = axis.size ) )
    } else {
        if(xaxis.draw) g <- g + theme( axis.line.x = element_line(color = axis.colour, size = axis.size ) )
        if(yaxis.draw) g <- g + theme( axis.line.y = element_line(color = axis.colour, size = axis.size ) )
    }
    if(ticks.draw)  g <- g + theme( axis.ticks = element_line(color = axis.colour, size = axis.size ) )
    if(hgrid.draw & vgrid.draw){
        g <- g + theme( panel.grid.major = element_line(colour = grids.colour, size = grids.size, linetype = grids.type ) )
    } else{
        if(vgrid.draw) g <- g + theme( panel.grid.major.x = element_line(colour = grids.colour, size = grids.size, linetype = grids.type ) ) 
        if(hgrid.draw) g <- g + theme( panel.grid.major.y = element_line(colour = grids.colour, size = grids.size, linetype = grids.type ) )
    }
   if(labels.rotate){
       g <- g + theme( axis.text.x = element_text(hjust = 1, angle = 45 ) )
   }
    return(g)
}
 
 
#===== MAPS ----------------------------------------------------------------------------------------------------------------
proj.wgs <- '+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0'
UK_centre <- c(-2.421976, 53.825564)
UK_bounds <- c(-8.3, 49.9, 1.8, 59.0 )
 
# List for the MAPTILES combo box 
tiles.list <- as.list(maptiles[, url])
names(tiles.list) <- maptiles[, name]
tile.ini <- tiles.list$CartoDB.Positron
loca.map <- c('CCG', 'LAT', 'NHSR', 'CCR', 'CTRY')
loca.ini <- 'LAT'
pal.ini <- 'Blues' # c("#CDC673", "#90EE90", "#20B2AA")
 
# icons for hospitals: green = nhs, red = private
hsp.icons <- awesomeIcons(
    icon = 'h-square',
    library = 'fa',
    squareMarker = TRUE,
    markerColor = sapply(centres$type, function(x) if(x == 1){ "lightgreen" } else { "lightred" }),
    iconColor = 'white'
)
 
# list of classification methods, to be used with classInt and ColorBrewer packages 
class.methods <- c(
#    'Fixed' = 'fixed',                  # need an additional argument fixedBreaks that lists the n+1 values to be used
    'Equal Intervals' = 'equal',        # the range of the variable is divided into n part of equal space
    'Quantiles' = 'quantile',           # each class contains (more or less) the same amount of values
#    'Pretty Integers' = 'pretty',       # sequence of about ‘n+1’ equally spaced ‘round’ values which cover the range of the values in ‘x’. The values are chosen so that they are 1, 2 or 5 times a power of 10.
    'Natural Breaks' = 'jenks',         # seeks to reduce the variance within classes and maximize the variance between classes
    'Hierarchical Cluster' = 'hclust',  # Cluster with short distance
    'K-means Cluster' = 'kmeans'        # Cluster with low variance and similar size
)
 
# Read boundaries as shapefiles from files in www directory
# boundaries <- lapply(loca.map, function(x) readOGR(shp.path, x))
# names(boundaries) <- loca.map
# for(m in loca.map){
#     boundaries[[m]] <- merge(boundaries[[m]], areas[, .(ons_id, nhs_id, name)], by.x = 'id', by.y = 'ons_id')
#     boundaries[[m]] <- merge(boundaries[[m]], centres[get(audit) == 1, .(H = .N), .(ons_id = get(paste0(m, '_ons')))], by.x = 'id', by.y = 'ons_id')
# }
# Read boundaries as unique list from rds shared rep
boundaries <- readRDS(paste0(data.path, 'boundaries.rds'))
for(m in loca.map){
    boundaries[[m]] <- merge(boundaries[[m]], centres[get(audit) == 1, .(H = .N), .(ons_id = get(paste0(m, '_ons')))], by.x = 'id', by.y = 'ons_id')
}
 
# Determines the text intervals for the colours in the map legend
get.legend.colnames <- function(bnd, mtc.type, lbl.brks, ncols) {
    if(mtc.type == 1){
        lbl.brks <- format(round(lbl.brks, 0), big.mark = ',')
    } else if(mtc.type == 2){
        lbl.brks <- format(round(100*lbl.brks, 2), nsmall = 2)
    } else {
        lbl.brks <- format(round(lbl.brks, 1), nsmall = 1)
    }
    lbl.text <- sapply(2:ncols,
        function(x)
            paste0(
                lbl.brks[x-1], ' \u2264 n < ', lbl.brks[x],
                ' (', length(bnd$Y[bnd$Y >= as.numeric(gsub(',', '', lbl.brks[x-1])) & bnd$Y < as.numeric(gsub(',', '', lbl.brks[x])) ] ), ')'
            )
    )
    lbl.text <- c(lbl.text,
        paste0(
            lbl.brks[ncols], ' \u2264 n \u2264 ', lbl.brks[ncols + 1],
            ' (', length(bnd$Y[bnd$Y >= as.numeric(gsub(',', '', lbl.brks[ncols])) & bnd$Y <= as.numeric(gsub(',', '', lbl.brks[ncols + 1])) ] ), ')'
        )
    )
}

Comments