diff --git a/DESCRIPTION b/DESCRIPTION
index 5b9a2a3..5447ef4 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -12,5 +12,8 @@ RoxygenNote: 7.3.3
Imports:
AVSDevR.DBClient,
dplyr,
+ htmltools,
magrittr,
- R6
+ plotly,
+ R6,
+ shiny
diff --git a/NAMESPACE b/NAMESPACE
index 8eee395..e733b4b 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -7,7 +7,11 @@ export(MNR.DB.Applications)
export(MNR.DB.Geometries)
export(MNR.DB.Organisations)
export(MNR.DB.Users)
+export(MNR.GeoPlot)
+export(resourcePrefix)
+export(use_mnr_ui)
import(dplyr)
+import(plotly)
importFrom(AVSDevR.DBClient,DBClient)
importFrom(AVSDevR.DBClient,DBConnection)
importFrom(R6,R6Class)
diff --git a/R/aaa.R b/R/aaa.R
index 6c859b6..5b223af 100644
--- a/R/aaa.R
+++ b/R/aaa.R
@@ -3,4 +3,5 @@
#' @importFrom AVSDevR.DBClient DBClient
#' @importFrom R6 R6Class
#' @import dplyr
+#' @import plotly
NULL
diff --git a/R/db_organisations.R b/R/db_organisations.R
index eef039b..99558b9 100644
--- a/R/db_organisations.R
+++ b/R/db_organisations.R
@@ -6,6 +6,7 @@ MNR.DB.Organisations <- R6::R6Class(
public = list(
getOrganisation = function(org_id, collect = TRUE) {
private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
dplyr::filter(id == !!org_id) %>%
private$db_client$collectOrReturn()
},
@@ -55,9 +56,37 @@ MNR.DB.Organisations <- R6::R6Class(
}
},
- getUsersFromOrg = function(org_name = "", org_id = -1, collect = TRUE) {
+ getOrganisationMembers = function(
+ org_name = "", org_id = -1, collect = TRUE
+ ) {
private$db_client$table("users") %>%
- dplyr::filter(flag_enabled == TRUE, flag_verified == TRUE) %>%
+ dplyr::filter(flag_enabled == TRUE) %>%
+ dplyr::filter(flag_verified == TRUE) %>%
+ dplyr::mutate(known_as = paste(first_name, last_name)) %>%
+ dplyr::left_join(
+ private$db_client$table("organisation_members") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
+ dplyr::select(user_id, organisation_id),
+ by = c(id = "user_id")
+ ) %>%
+ dplyr::inner_join(
+ private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
+ dplyr::filter((id == !!org_id) | (name == !!org_name)) %>%
+ dplyr::select(id),
+ by = c(organisation_id = "id")
+ ) %>%
+ dplyr::select(-organisation_id) %>%
+ private$db_client$collectOrReturn()
+ },
+
+ #' @depricated
+ getUsersFromOrg = function(org_name = "", org_id = -1, collect = TRUE) {
+ .Deprecated(new = "getOrganisationMembers")
+
+ private$db_client$table("users") %>%
+ dplyr::filter(flag_enabled == TRUE) %>%
+ dplyr::filter(flag_verified == TRUE) %>%
dplyr::mutate(known_as = paste(first_name, last_name)) %>%
dplyr::left_join(
private$db_client$table("organisation_members") %>%
@@ -76,6 +105,7 @@ MNR.DB.Organisations <- R6::R6Class(
isAgent = function(user_id) {
private$db_client$table("organisation_members") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
dplyr::filter(user_id == !!user_id) %>%
dplyr::inner_join(
private$db_client$table("organisation_agents") %>%
@@ -87,9 +117,54 @@ MNR.DB.Organisations <- R6::R6Class(
magrittr::is_greater_than(0)
},
+ getOrganisationAgents = function(org_id, as_int = FALSE, collect = TRUE) {
+ qry <- private$db_client$table("organisation_agents") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
+ dplyr::filter(organisation_id == !!org_id) %>%
+ dplyr::select(id = agent_id) %>%
+ dplyr::distinct() %>%
+ dplyr::left_join(
+ private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE),
+ by = "id"
+ )
+
+ if (as_int) {
+ qry %>%
+ dplyr::pull(id)
+ } else {
+ qry %>%
+ private$db_client$collectOrReturn()
+ }
+ },
+
+ getAgentRecipients = function(agent_id, as_int = FALSE, collect = TRUE) {
+ qry <- private$db_client$table("organisation_agents") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
+ dplyr::filter(agent_id == !!agent_id) %>%
+ dplyr::select(id = organisation_id) %>%
+ dplyr::distinct() %>%
+ dplyr::left_join(
+ private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE),
+ by = "id"
+ )
+
+ if (as_int) {
+ qry %>%
+ dplyr::pull(id)
+ } else {
+ qry %>%
+ private$db_client$collectOrReturn()
+ }
+ },
+
+ #' @depricated
getAgentForOrganisations = function(
org_ids, as_int = FALSE, name_only = TRUE, collect = TRUE
) {
+ .Deprecated(new = "getOrganisationAgents")
+
qry <- private$db_client$table("organisations") %>%
dplyr::filter(flag_approved == TRUE) %>%
dplyr::inner_join(
@@ -113,9 +188,12 @@ MNR.DB.Organisations <- R6::R6Class(
}
},
+ #' @depricated
getRecipientOrganisations = function(
org_id, as_int = FALSE, name_only = TRUE, collect = TRUE
) {
+ .Deprecated(new = "getAgentRecipients")
+
qry <- private$db_client$table("organisations") %>%
dplyr::filter(flag_approved == TRUE) %>%
dplyr::inner_join(
@@ -139,16 +217,19 @@ MNR.DB.Organisations <- R6::R6Class(
}
},
+ #' @depricated
getUserOrgs = function(
user_id, as_int = TRUE, name_only = TRUE, collect = TRUE
) {
+ .Deprecated(new = "getUserOrganisation")
+
qry <- private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
dplyr::left_join(
private$db_client$table("organisation_members") %>%
dplyr::select(user_id, organisation_id),
by = c(id = "organisation_id")
) %>%
- dplyr::filter(flag_approved == TRUE) %>%
dplyr::filter(user_id == !!user_id)
if (as_int) {
@@ -167,12 +248,13 @@ MNR.DB.Organisations <- R6::R6Class(
user_id, as_int = TRUE, name_only = TRUE, collect = TRUE
) {
qry <- private$db_client$table("organisations") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
dplyr::left_join(
private$db_client$table("organisation_members") %>%
+ dplyr::filter(flag_approved == TRUE) %>%
dplyr::select(user_id, organisation_id),
by = c(id = "organisation_id")
) %>%
- dplyr::filter(flag_approved == TRUE) %>%
dplyr::filter(user_id == !!user_id) %>%
dplyr::slice_min(user_id, n = 1)
diff --git a/R/db_users.R b/R/db_users.R
index fbba3c9..c2581f1 100644
--- a/R/db_users.R
+++ b/R/db_users.R
@@ -14,13 +14,13 @@ MNR.DB.Users <- R6::R6Class(
},
getUser = function(user_id, collect = TRUE) {
- db_uf$getUser(user_id, collect)
+ private$db_uf$getUser(user_id, collect)
},
getUserName = function(user_id) {
- db_uf$getUserName(user_id)
+ private$db_uf$getUserName(user_id)
},
getUserRoles = function(user_id, collect = TRUE) {
- db_uf$getUserRoles(user_id, collect)
+ private$db_uf$getUserRoles(user_id, collect)
},
isApplicationsAdmin = function(user_id) {
diff --git a/R/geoPlot.R b/R/geoPlot.R
new file mode 100644
index 0000000..a2d5d36
--- /dev/null
+++ b/R/geoPlot.R
@@ -0,0 +1,382 @@
+#' @export
+# nolint next: object_name_linter. R6Class
+MNR.GeoPlot <- R6::R6Class(
+ "MNR.GeoPlot",
+ public = list(
+ initialize = function() {
+ private$bootGridLines()
+ },
+
+ bootLayers = function() {
+ private$boundaryIoM <- sf::st_read(
+ file.path(
+ system.file(package = "AVSDevR.MarineNoiseRegistry"),
+ "layers",
+ "20230411_IoM"
+ ),
+ quiet = TRUE
+ )
+ private$boundaryMSFD <- sf::st_read(
+ file.path(
+ system.file(package = "AVSDevR.MarineNoiseRegistry"),
+ "layers",
+ "20250123_MSFD_BoundaryLine"
+ ),
+ quiet = TRUE
+ )
+ private$boundaryUKCS <- sf::st_read(
+ file.path(
+ system.file(package = "AVSDevR.MarineNoiseRegistry"),
+ "layers",
+ "20230411_UKCSBoundary"
+ ),
+ quiet = TRUE
+ )
+ private$boundaryUKTerritorial <- sf::st_read(
+ file.path(
+ system.file(package = "AVSDevR.MarineNoiseRegistry"),
+ "layers",
+ "20250123_UK_Territorial_Sea_Limit"
+ ),
+ quiet = TRUE
+ )
+ },
+
+ bootQuadrants = function() {
+ old_opts <- options(sf_use_s2 = FALSE)
+ on.exit({
+ options(old_opts)
+ })
+
+ private$quadrants <- sf::st_read(
+ file.path(
+ system.file(package = "AVSDevR.MarineNoiseRegistry"),
+ "layers",
+ "20230411_OGBQuadrants"
+ ),
+ quiet = TRUE
+ )
+ private$quadrant_annotations <- suppressWarnings({
+ private$quadrants %>%
+ dplyr::select(quadrnt) %>%
+ sf::st_centroid()
+ })
+ },
+
+ bootConservationAreas = function(db_client) {
+ old_opts <- options(sf_use_s2 = FALSE)
+ on.exit({
+ options(old_opts)
+ })
+
+ private$conservation_areas <- db_client$table("conservation_areas") %>%
+ dplyr::select(name, season, geom) %>%
+ db_client$collectGeometries() %>%
+ sf::st_sf() %>%
+ sf::st_cast("MULTIPOLYGON")
+
+ colour_palette <- private$conservation_areas %>%
+ dplyr::mutate(
+ colour = sapply(season, function(s) {
+ switch(
+ s,
+ Winter = "rgba(30,203,225,0.3)",
+ Summer = "rgba(225,52,30,0.3)",
+ "rgba(235,175,20,0.3)"
+ )
+ })
+ ) %>%
+ dplyr::pull(colour)
+
+ private$conservation_area_colours <- private$conservation_areas %>%
+ sf::st_coordinates() %>%
+ tibble::as_tibble() %>%
+ dplyr::mutate(colour = colour_palette[L3]) %>%
+ dplyr::pull(colour)
+ },
+
+ makeBasePlot = function(
+ ..., with_jncc_layers = TRUE, with_quadrants = FALSE,
+ with_conservation_areas = FALSE
+ ) {
+ do.call(plotly::plot_ly, list(...)) %>%
+ private$addGridLines() %>%
+ private$addQuadrants(with_quadrants) %>%
+ private$addJNCCLayers(with_jncc_layers) %>%
+ private$addConservationAreas(with_conservation_areas) %>%
+ plotly::layout(
+ mapbox = list(style = "carto-positron"),
+ legend = list(
+ groupclick = "toggleitem",
+ itemdoubleclick = FALSE
+ ),
+ xaxis = list(
+ title = list(text = "Longitude", font = list(size = 18)),
+ visible = FALSE,
+ # Grid:
+ showgrid = TRUE,
+ gridcolor = "#BEBEBE",
+ # Line:
+ showline = TRUE,
+ linewidth = 2,
+ linecolor = "#7F7F7F",
+ mirror = TRUE,
+ zeroline = FALSE,
+ # Ticks:
+ showticklabels = TRUE,
+ tickmode = "linear",
+ tick0 = 0,
+ dtick = 5,
+ ticksuffix = " ° W"
+ ),
+ yaxis = list(
+ title = list(text = "Latitude", font = list(size = 18)),
+ visible = FALSE,
+ # Grid:
+ showgrid = TRUE,
+ gridcolor = "#BEBEBE",
+ # Line:
+ showline = TRUE,
+ linewidth = 2,
+ linecolor = "#7F7F7F",
+ mirror = TRUE,
+ zeroline = FALSE,
+ # Ticks:
+ showticklabels = TRUE,
+ tickmode = "linear",
+ tick0 = 0,
+ dtick = 5,
+ ticksuffix = " ° N"
+ )
+ )
+ },
+
+ boundMap = function(
+ p, c_lat = 56, c_lon = -5.5, zoom = 3.5, xmin = -25, ymin = 45, xmax = 5,
+ ymax = 65
+ ) {
+ p %>%
+ plotly::layout(
+ mapbox = list(
+ zoom = zoom,
+ center = list(lat = c_lat, lon = c_lon),
+ `_fitBounds` = list(
+ bounds = list(list(xmin, ymin), list(xmax, ymax)),
+ options = list()
+ )
+ )
+ )
+ }
+ ),
+ private = list(
+ major_lines = NULL,
+ minor_lines = NULL,
+ zero_lines = NULL,
+
+ boundaryIoM = NULL,
+ boundaryMSFD = NULL,
+ boundaryUKCS = NULL,
+ boundaryUKTerritorial = NULL,
+
+ quadrants = NULL,
+ quadrant_annotations = NULL,
+
+ conservation_areas = NULL,
+ conservation_area_colours = NULL,
+
+
+ bootGridLines = function() {
+ old_opts <- options(sf_use_s2 = FALSE)
+ on.exit({
+ options(old_opts)
+ })
+
+ points <- tibble::tibble()
+ for (lat in seq(-90, +90, 5)) {
+ points <- points %>%
+ dplyr::bind_rows(
+ tibble::tibble(
+ grp = nrow(points) + 1,
+ lat = lat,
+ lon = c(-180, 180),
+ is_major = (lat %% 10) == 0
+ )
+ )
+ }
+ for (lon in seq(-180, +175, 5)) {
+ points <- points %>%
+ dplyr::bind_rows(
+ tibble::tibble(
+ grp = nrow(points) + 1,
+ lat = c(-90, 90),
+ lon = lon,
+ is_major = (lat %% 10) == 0
+ )
+ )
+ }
+ suppressMessages({
+ private$major_lines <- points %>%
+ dplyr::filter(is_major) %>%
+ sf::st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
+ dplyr::group_by(grp) %>%
+ dplyr::summarise() %>%
+ sf::st_cast("LINESTRING")
+ private$minor_lines <- points %>%
+ dplyr::filter(!is_major) %>%
+ sf::st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
+ dplyr::group_by(grp) %>%
+ dplyr::summarise() %>%
+ sf::st_cast("LINESTRING")
+ private$zero_lines <- points %>%
+ dplyr::filter((lat == 0) | (lon == 0)) %>%
+ sf::st_as_sf(coords = c("lon", "lat"), crs = 4326) %>%
+ dplyr::group_by(grp) %>%
+ dplyr::summarise() %>%
+ sf::st_cast("LINESTRING")
+ })
+ },
+
+ addGridLines = function(p) {
+ p %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$minor_lines,
+ name = "minor_grid_lines",
+ line = list(width = 1),
+ color = I("#6161ff20"),
+ showlegend = FALSE,
+ hoverinfo = I("skip")
+ ) %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$major_lines,
+ name = "major_grid_lines",
+ line = list(width = 2),
+ color = I("#6161ff30"),
+ showlegend = FALSE,
+ hoverinfo = I("skip")
+ ) %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$zero_lines,
+ name = "zero_grid_lines",
+ line = list(width = 2),
+ color = I("#3131ff40"),
+ showlegend = FALSE,
+ hoverinfo = I("skip")
+ )
+ },
+
+ addJNCCLayers = function(p, with_jncc_layers) {
+ if (length(with_jncc_layers) == 0 || !with_jncc_layers) {
+ return(p)
+ }
+ if (is.null(private$boundaryIoM)) {
+ rlang::abort(
+ "JNCC layers have not been loaded. Call bootLayers first!"
+ )
+ }
+ p %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = dplyr::bind_rows(
+ sf::st_cast(private$boundaryUKCS, "LINESTRING", warn = FALSE),
+ sf::st_cast(private$boundaryIoM, "LINESTRING", warn = FALSE),
+ sf::st_cast(private$boundaryMSFD, "LINESTRING", warn = FALSE)
+ ),
+ fillcolor = "#96005b00",
+ line = list(width = 1.5),
+ color = I("#96005b"),
+ name = "UKMS Sub-region Borders",
+ legendgrouptitle = list(text = "Regional Boundaries"),
+ legendgroup = "regional_boundaries",
+ legendrank = 850,
+ hoverinfo = I("skip")
+ ) %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$boundaryUKCS,
+ fillcolor = "#00000000",
+ line = list(width = 1.5),
+ color = I("#000000"),
+ name = "UK Continental Shelf",
+ legendgrouptitle = list(text = "Regional Boundaries"),
+ legendgroup = "regional_boundaries",
+ legendrank = 800,
+ hoverinfo = I("skip")
+ ) %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$boundaryUKTerritorial,
+ fillcolor = "#008ac100",
+ line = list(width = 1),
+ color = I("#008ac1"),
+ name = "UK Territorial Sea Limit",
+ legendgrouptitle = list(text = "Regional Boundaries"),
+ legendgroup = "regional_boundaries",
+ legendrank = 800,
+ hoverinfo = I("skip")
+ )
+ },
+
+ addQuadrants = function(p, with_quadrants) {
+ if (length(with_quadrants) == 0 || !with_quadrants) {
+ return(p)
+ }
+ if (is.null(private$quadrants)) {
+ rlang::abort(
+ "Quadrants have not been loaded. Call bootQuadrants first!"
+ )
+ }
+ p %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$quadrants,
+ fillcolor = "rgba(0,0,0,0)",
+ line = list(width = 1),
+ color = I("#bebebe"),
+ name = "Oil & Gas Quadrants",
+ legendgrouptitle = list(text = "Oil & Gas"),
+ legendgroup = "oil_and_gas",
+ legendrank = 900,
+ hoverinfo = I("none")
+ ) %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$quadrant_annotations,
+ color = I("#bebebe00"),
+ name = "Oil & Gas Quadrant Labels",
+ showlegend = FALSE,
+ text = ~quadrnt,
+ hoverinfo = I("text"),
+ hovertemplate = I("O&G Quadrant: %{text}")
+ )
+ },
+
+ addConservationAreas = function(p, with_conservation_areas) {
+ if (length(with_conservation_areas) == 0 || !with_conservation_areas) {
+ return(p)
+ }
+ if (is.null(private$conservation_areas)) {
+ rlang::abort(
+ "Conservation Areas have not been loaded. \
+ Call bootConservationAreas first!"
+ )
+ }
+ p %>%
+ plotly::add_sf(
+ type = "scattermapbox",
+ data = private$conservation_areas,
+ fillcolor = private$conservation_area_colours,
+ line = list(width = 0),
+ color = I(private$conservation_area_colours),
+ name = ~paste0(name, " (", season, ")"),
+ legendgrouptitle = list(
+ text = "Special Areas of Conservation"
+ ),
+ legendgroup = "conservation_areas",
+ legendrank = 950
+ )
+ }
+ )
+)
diff --git a/R/use_mnr_ui.R b/R/use_mnr_ui.R
new file mode 100644
index 0000000..d7c0cd2
--- /dev/null
+++ b/R/use_mnr_ui.R
@@ -0,0 +1,36 @@
+#' @export
+use_mnr_ui <- function() {
+ attachResourcePaths()
+ htmltools::tags$head(
+ htmltools::singleton(
+ htmltools::HTML(paste0(
+ ""
+ ))
+ ),
+ htmltools::singleton(
+ htmltools::HTML(paste0(
+ ""
+ ))
+ ),
+ htmltools::singleton(
+ htmltools::HTML(paste0(
+ ""
+ ))
+ )
+ )
+}
+
+#' @export
+resourcePrefix <- function() {
+ paste0("avsdev_mnr_", utils::packageVersion("AVSDevR.MarineNoiseRegistry"))
+}
+
+attachResourcePaths <- function() {
+ if (!(resourcePrefix() %in% names(shiny::resourcePaths()))) {
+ shiny::addResourcePath(
+ resourcePrefix(),
+ system.file("www", package = "AVSDevR.MarineNoiseRegistry")
+ )
+ }
+}
diff --git a/R/zzz.R b/R/zzz.R
new file mode 100644
index 0000000..c7df506
--- /dev/null
+++ b/R/zzz.R
@@ -0,0 +1,3 @@
+.onAttach <- function(libname, pkgname) {
+ attachResourcePaths()
+}
diff --git a/inst/layers/20230411_IoM/20230411_IoM_WGS84.dbf b/inst/layers/20230411_IoM/20230411_IoM_WGS84.dbf
new file mode 100644
index 0000000..689391f
Binary files /dev/null and b/inst/layers/20230411_IoM/20230411_IoM_WGS84.dbf differ
diff --git a/inst/layers/20230411_IoM/20230411_IoM_WGS84.prj b/inst/layers/20230411_IoM/20230411_IoM_WGS84.prj
new file mode 100644
index 0000000..f45cbad
--- /dev/null
+++ b/inst/layers/20230411_IoM/20230411_IoM_WGS84.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/inst/layers/20230411_IoM/20230411_IoM_WGS84.shp b/inst/layers/20230411_IoM/20230411_IoM_WGS84.shp
new file mode 100644
index 0000000..1c94f6d
Binary files /dev/null and b/inst/layers/20230411_IoM/20230411_IoM_WGS84.shp differ
diff --git a/inst/layers/20230411_IoM/20230411_IoM_WGS84.shx b/inst/layers/20230411_IoM/20230411_IoM_WGS84.shx
new file mode 100644
index 0000000..8917335
Binary files /dev/null and b/inst/layers/20230411_IoM/20230411_IoM_WGS84.shx differ
diff --git a/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.dbf b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.dbf
new file mode 100644
index 0000000..c751c1f
Binary files /dev/null and b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.dbf differ
diff --git a/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.prj b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.prj
new file mode 100644
index 0000000..79a08dc
--- /dev/null
+++ b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433],METADATA["World",-180.0,-90.0,180.0,90.0,0.0,0.0174532925199433,0.0,1262]]
\ No newline at end of file
diff --git a/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shp b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shp
new file mode 100644
index 0000000..7930261
Binary files /dev/null and b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shp differ
diff --git a/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shx b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shx
new file mode 100644
index 0000000..2eabbc7
Binary files /dev/null and b/inst/layers/20230411_OGBQuadrants/20230411_OGBQ_WGS84.shx differ
diff --git a/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.dbf b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.dbf
new file mode 100644
index 0000000..689391f
Binary files /dev/null and b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.dbf differ
diff --git a/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.prj b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.prj
new file mode 100644
index 0000000..f45cbad
--- /dev/null
+++ b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shp b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shp
new file mode 100644
index 0000000..0803e90
Binary files /dev/null and b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shp differ
diff --git a/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shx b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shx
new file mode 100644
index 0000000..253286c
Binary files /dev/null and b/inst/layers/20230411_UKCSBoundary/20230411_UKCSBoundary_WGS84.shx differ
diff --git a/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.dbf b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.dbf
new file mode 100644
index 0000000..6b256a1
Binary files /dev/null and b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.dbf differ
diff --git a/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.prj b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.prj
new file mode 100644
index 0000000..f45cbad
--- /dev/null
+++ b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shp b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shp
new file mode 100644
index 0000000..2e29c88
Binary files /dev/null and b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shp differ
diff --git a/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shx b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shx
new file mode 100644
index 0000000..f1fee68
Binary files /dev/null and b/inst/layers/20250123_MSFD_BoundaryLine/20170202_MSFD_BL_WGS84.shx differ
diff --git a/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.dbf b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.dbf
new file mode 100644
index 0000000..2a96c37
Binary files /dev/null and b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.dbf differ
diff --git a/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.prj b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.prj
new file mode 100644
index 0000000..f45cbad
--- /dev/null
+++ b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.prj
@@ -0,0 +1 @@
+GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
\ No newline at end of file
diff --git a/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shp b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shp
new file mode 100644
index 0000000..1f08be7
Binary files /dev/null and b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shp differ
diff --git a/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shx b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shx
new file mode 100644
index 0000000..64187f7
Binary files /dev/null and b/inst/layers/20250123_UK_Territorial_Sea_Limit/20250123_UK_TSL_WGS84.shx differ
diff --git a/inst/www/css/mnr-shiny.css b/inst/www/css/mnr-shiny.css
new file mode 100644
index 0000000..8199d90
--- /dev/null
+++ b/inst/www/css/mnr-shiny.css
@@ -0,0 +1,8 @@
+div.selectize-input > div.item {
+ line-height: 41px;
+}
+
+input[type="text"], input[type="password"], input[type="number"] {
+ font-size: 12pt !important;
+ height: 41px !important;
+}
\ No newline at end of file
diff --git a/inst/www/css/mnr.css b/inst/www/css/mnr.css
new file mode 100644
index 0000000..d528843
--- /dev/null
+++ b/inst/www/css/mnr.css
@@ -0,0 +1,567 @@
+/* JNCC preferred font */
+*, body, h1, h2, h3, h4, h5, h6, th {
+ font-family: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif;
+}
+body, th {
+ /* font-size: 14.25pt; */
+ font-size: 12pt;
+}
+
+h1 {
+ font-size: 31.5pt;
+}
+h2 {
+ font-size: 22.5pt;
+}
+h3 {
+ font-size: 21pt;
+}
+h4 {
+ font-size: 19pt;
+}
+h5 {
+ font-size: 15pt;
+}
+h6 {
+ font-size: 12pt;
+}
+
+.tablesorter-bootstrap tfoot td, .tablesorter-bootstrap tfoot th, .tablesorter-bootstrap thead td, .tablesorter-bootstrap thead th {
+ font-size: 12pt !important;
+}
+
+/*
+ * Skin: Green - JNCC
+ * -----------
+ */
+.skin-green .main-header .navbar {
+ background-color: #3f9c35 !important;
+}
+.skin-green .main-header .navbar .sidebar-toggle:hover {
+ background-color: #36822c !important;
+}
+@media (max-width: 767px) {
+ .skin-green .main-header .navbar .dropdown-menu li a:hover {
+ background: #36822c !important;
+ }
+}
+.skin-green .main-header .logo {
+ background-color: #36822c !important;
+}
+.skin-green .main-header .logo:hover {
+ background-color: #337a2a !important;
+}
+.skin-green .main-header li.user-header {
+ background-color: #3f9c35 !important;
+}
+.skin-green .sidebar-menu > li.active > a {
+ border-left-color: #3f9c35 !important;
+}
+
+.widget-user-header.bg-green {
+ background-color: #3f9c35 !important;
+}
+
+.bg-success, .alert-success, .bg-green {
+ background-color: #3f9c35 !important;
+}
+
+.box-success {
+ border: 1px solid #3f9c35 !important;
+}
+
+.box-success > .box-header {
+ background-color: #3f9c35 !important;
+}
+
+.btn-success {
+ background-color: #3f9c35 !important;
+}
+.btn-success:hover {
+ background-color: #008d4c !important;
+}
+.box.box-solid > .box-header .btn:hover, .box.box-solid > .box-header a:hover {
+ background: rgba(0, 0, 0, 0.1) !important;
+}
+
+
+.table.dataTable tbody td.active,
+.table.dataTable tbody tr.active td {
+ background-color: #3f9c35 !important;
+}
+
+table.dataTable tbody tr.selected > * {
+ box-shadow: inset 0 0 0 9999px #3f9c35 !important;
+}
+
+table.dataTable.hover tbody tr.selected:hover > *,
+table.dataTable.display tbody tr.selected:hover > * {
+ box-shadow: inset 0 0 0 9999px #44a939 !important;
+}
+
+/* Cookie control banner */
+.ccc-notify-text {
+ margin-top: 7em;
+ margin-bottom: 7em;
+ padding-right: 2em;
+}
+
+
+/* AdminLTE overrides - Including for accessibility*/
+.callout.callout-warning, .callout.callout-info, .alert-warning, .alert-info {
+ font-size: 18pt;
+}
+.callout.callout-success, .alert-success {
+ font-size: 18pt;
+ /* background-color: #36822c !important; */
+}
+.callout.callout-danger, .alert-danger, .alert-error {
+ background-color: #DA3A25 !important;
+}
+
+.alert .close {
+ font-size: 30pt;
+ top: -15px;
+}
+
+.main-header {
+ position: fixed;
+ top: 0;
+ width: 100%;
+}
+.main-header .logo {
+ font-size: 15pt;
+ width: 270px;
+}
+@media (max-width: 767px) {
+ .main-header .logo, .main-header .navbar {
+ width: 100%;
+ }
+}
+.main-header .navbar {
+ margin-left: 270px;
+}
+@media (max-width: 767px) {
+ .main-header .navbar {
+ margin-left: 0px;
+ }
+}
+.main-sidebar {
+ position: fixed;
+ top: 0;
+ width: 270px;
+}
+@media (max-width: 767px) {
+ .main-sidebar {
+ -webkit-transform: translate(-270px, 0);
+ -ms-transform: translate(-270px, 0);
+ -o-transform: translate(-270px, 0);
+ transform: translate(-270px, 0);
+ }
+}
+.main-header .sidebar-toggle {
+ line-height: 20px;
+}
+.main-sidebar .user-panel .info p {
+ white-space: break-spaces;
+}
+.sidebar-menu {
+ /* height: calc(100vh - 125px); */
+ position: fixed;
+ width: 270px;
+ overflow-y: scroll;
+ top: 125px;
+ bottom: 0;
+ height: auto;
+ -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
+}
+.sidebar-menu:hover {
+ overflow-y: scroll;
+}
+.sidebar-mini.sidebar-collapse .sidebar-menu {
+ width: 50px;
+ -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out;
+ transition: transform 0.3s ease-in-out, width 0.3s ease-in-out;
+}
+@media (max-width: 767px) {
+ .sidebar-menu {
+ top: 175px;
+ }
+}
+
+@media (max-width: 767px) {
+ .sidebar-open .content-wrapper, .sidebar-open .main-footer {
+ -webkit-transform: translate(270px, 0);
+ -ms-transform: translate(270px, 0);
+ -o-transform: translate(270px, 0);
+ transform: translate(270px, 0);
+ }
+}
+.content-header h1 {
+ font-size: 30pt;
+}
+.content-wrapper {
+ margin-top: 50px;
+}
+@media (max-width: 767px) {
+ .content-wrapper {
+ margin-top: 100px;
+ }
+}
+.content-wrapper, .main-footer {
+ margin-left: 270px;
+}
+@media (max-width: 767px) {
+ .content-wrapper, .main-footer {
+ margin-left: 0;
+ }
+}
+
+.widget-user-desc a {
+ font-size: 14pt;
+ color: #FFFFFF;
+ font-weight: bold;
+}
+.widget-user-desc a:hover {
+ color: #BBBBFF;
+}
+
+.widget-user-2 .widget-user-username {
+ font-weight: bolder;
+}
+
+/* AdminLTE extensions */
+.widget-user-3 .widget-user-header {
+ padding: 20px;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+.widget-user-3 h1 {
+ padding: 0px;
+ margin: 0px;
+ font-size: 18pt;
+}
+
+.login-box-footer {
+ margin-top: 20px;
+}
+
+/* UF extensions */
+.page-description {
+ font-size: 14pt;
+ padding-top: 4px;
+ color: #6D6D6D;
+}
+
+
+
+/* Navbar tweaks */
+.landing-nav > .container{
+ display: flex;
+}
+.navbar-spacer {
+ display: inline-block;
+ flex-grow: 1;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ line-height: 20px;
+ color: #ffffff;
+ font-weight: bolder;
+ z-index: 900;
+}
+.landing-nav .collapse {
+ float: right;
+}
+.landing-nav .navbar-spacer {
+ display: none;
+}
+.landing-nav > .container > .navbar-spacer {
+ display: inline-block;
+}
+@media (max-width: 767px) {
+ .landing-nav > .container{
+ display: inherit;
+ }
+ .landing-nav .collapse {
+ float: none;
+ }
+ .landing-nav .navbar-spacer {
+ display: inline-block;
+ }
+ .landing-nav > .container > .navbar-spacer {
+ display: none;
+ }
+}
+
+.landing-nav > .container{
+ display: flex;
+}
+.navbar-beta {
+ display: inline-block;
+ flex-grow: 1;
+ padding-top: 15px;
+ padding-bottom: 15px;
+ line-height: 20px;
+ color: #ffffff;
+ font-weight: bolder;
+ z-index: 900;
+}
+.navbar-brand .navbar-beta {
+ font-size: 18pt;
+ font-weight: normal;
+}
+.navbar-beta a {
+ color: #ffffff;
+}
+.landing-nav .collapse {
+ float: right;
+}
+.landing-nav.navbar-beta {
+ font-size: 18pt;
+ display: none;
+}
+.landing-nav > .container > .navbar-beta {
+ display: inline-block;
+}
+@media (max-width: 767px) {
+ .landing-nav > .container {
+ display: inherit;
+ }
+ .landing-nav .collapse {
+ float: none;
+ }
+ .landing-nav.navbar-beta {
+ display: inline-block;
+ }
+ .navbar-brand .navbar-beta {
+ display: none;
+ }
+ .landing-nav > .container > .navbar-beta {
+ display: none;
+ }
+}
+
+@media (min-width: 900px) {
+ .public-app-frame {
+ width: 90vw;
+ }
+}
+@media (max-width: 900px) {
+ .public-app-frame {
+ width: 100vw;
+ }
+}
+
+/* Cookies table */
+.table-cookies {
+ border: 1px solid #888888 !important;
+ max-width: 80%;
+ margin-left: auto;
+ margin-right: auto;
+}
+.table-cookies th, .table-cookies td {
+ border: 1px solid #888888 !important;
+}
+.table-cookies thead th, .table-cookies thead td {
+ border-bottom-width: 2px !important;
+}
+
+/* Good/Bad list styling */
+ul.good-list {
+ list-style-type: '\2713';
+}
+ul.good-list li::marker {
+ color: #3c763d;
+ font-size: 24px;
+ line-height: 1.1;
+}
+
+ul.bad-list {
+ list-style-type: '\2717';
+}
+ul.bad-list li::marker {
+ color: #a94442;
+ font-size: 24px;
+ line-height: 1.1;
+}
+
+/* */
+.anchor-location {
+ padding-top: 60px;
+ margin-top: -60px;
+}
+@media (max-width: 767px) {
+ .anchor-location {
+ padding-top: 120px;
+ margin-top: -120px;
+ }
+}
+
+.switch {
+ top: 5px;
+}
+
+.switch-label {
+ font-weight: normal;
+}
+
+.modal-title {
+ font-size: 21pt;
+}
+
+/* Accessibility overrides */
+a {
+ color: #286080;
+}
+p a, h1 a, h2 a, h3 a, h4 a, td a, .content li a, .message-banner a, .link {
+ color: #286080;
+ text-decoration: underline;
+}
+.dropdown-menu a {
+ color: #286080;
+ text-decoration: none !important;
+}
+a:hover,
+a:active,
+a:focus {
+ text-decoration: underline;
+ color: #72afd2;
+}
+.login-box-footer a {
+ text-decoration: underline;
+}
+
+.btn {
+ font-size: 14pt;
+ /*
+ font-size: 12pt;
+ */
+ font-weight: bold;
+}
+
+.btn-info {
+ background-color: #009FC7;
+}
+.btn-warning {
+ background-color: #CB820B;
+}
+
+.text-muted {
+ color: #6D6D6D;
+}
+
+.login-box-body h2 {
+ font-size: 16pt;
+ font-weight: bold;
+}
+
+input[type="text"], input[type="password"], input[type="number"] {
+ /*
+ font-size: 14pt !important;
+ height: 41px !important;
+ */
+ font-size: 12pt !important;
+ height: 37px !important;
+}
+
+.content-jump-link {
+ position: absolute;
+ left: -10000px;
+ top: auto;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+}
+.content-jump-link:focus {
+ position: static;
+ width: auto;
+ height: auto;
+}
+
+.box-header > i {
+ font-weight: bolder;
+ font-size: 18pt !important;
+}
+
+.box-title {
+ font-weight: bolder;
+ font-size: 18pt !important;
+}
+
+.breadcrumb li {
+ font-size: 14pt;
+ color: #6B6B6B;
+}
+.breadcrumb li:active {
+ font-size: 14pt;
+ color: #6B6B6B;
+}
+.breadcrumb > .active {
+ color: #6B6B6B;
+}
+.breadcrumb > li + li::before {
+ content: "/\00a0" !important;
+ color: #6B6B6B;
+}
+
+.dropdown-menu {
+ font-size: 12pt;
+}
+
+.navbar-brand {
+ font-size: 24pt;
+ line-height: 27.15px;
+ padding: 0px;
+}
+@media (max-width: 767px) {
+ .navbar-brand {
+ font-size: 20pt;
+ padding: 15px;
+ }
+}
+.navbar-brand a {
+ color: #FFFFFF;
+}
+
+.navbar .nav > li > a {
+ font-size: 14pt;
+ font-weight: bold;
+}
+
+.navbar .nav .dropdown-menu a {
+ font-size: 14pt;
+}
+
+.sidebar-toggle {
+ font-size: 14pt;
+}
+
+.login-logo a {
+ text-decoration: none;
+}
+
+.skin-green .sidebar-menu > li.header {
+ color: #70979E;
+ font-weight: bolder;
+}
+
+.uf-table-info {
+ color: #757575;
+}
+
+.label {
+ padding-top: 0.5em;
+}
+
+.btn-xs, .btn-group-xs > .btn {
+ padding: 1px 5px;
+}
+
+.badge {
+ padding: 6px 9px 3px 9px;
+}
\ No newline at end of file
diff --git a/inst/www/images/dolphin.svg b/inst/www/images/dolphin.svg
new file mode 100644
index 0000000..cabee23
--- /dev/null
+++ b/inst/www/images/dolphin.svg
@@ -0,0 +1,44 @@
+
+
+
+
diff --git a/inst/www/js/frameHeight.js b/inst/www/js/frameHeight.js
new file mode 100644
index 0000000..4e9f246
--- /dev/null
+++ b/inst/www/js/frameHeight.js
@@ -0,0 +1,7 @@
+window.addEventListener('message', function (event) {
+ if (event.data == "FrameHeight") {
+ var body = document.body, html = document.documentElement;
+ var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
+ event.source.postMessage({ "FrameHeight": height }, "*");
+ }
+});
\ No newline at end of file