Upload 2 files
Browse files- depth_interactive_map_alpha.R +116 -4
- styles.css +40 -0
depth_interactive_map_alpha.R
CHANGED
|
@@ -864,6 +864,11 @@ WEB_SPEC <- list(
|
|
| 864 |
# --- Lucide icons + JS helpers --------------------------------------------------
|
| 865 |
LUCIDE_JS <- tags$head(
|
| 866 |
tags$script(src = "https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 867 |
tags$script(HTML("
|
| 868 |
// gridMenuOpen = is the N4-N8 resolution menu visible (purely visual);
|
| 869 |
// gridActive = is the grid overlay currently shown on the map.
|
|
@@ -910,6 +915,13 @@ LUCIDE_JS <- tags$head(
|
|
| 910 |
Shiny.setInputValue('grid_nivel', '6', { priority: 'event' });
|
| 911 |
}, 300);
|
| 912 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 913 |
// Hook up the native draw buttons so our custom ones can mirror their
|
| 914 |
// active state. Retries until mapgl has mounted the draw control.
|
| 915 |
var navTries = 0;
|
|
@@ -955,6 +967,99 @@ LUCIDE_JS <- tags$head(
|
|
| 955 |
if (tip) tip.style.display = 'none';
|
| 956 |
}
|
| 957 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
function clearDrawing() {
|
| 959 |
var btn = document.getElementById('clear_btn');
|
| 960 |
if (btn) btn.click();
|
|
@@ -1862,10 +1967,10 @@ ui <- page_fillable(
|
|
| 1862 |
class = "grid-nivel-item",
|
| 1863 |
`data-nivel` = nv,
|
| 1864 |
onclick = paste0("setGridNivel('", nv, "')"),
|
| 1865 |
-
`aria-label` = paste0("Set grid to ", info$name, ", ", info$size, "
|
| 1866 |
div(class = "grid-nivel-item-row",
|
| 1867 |
span(class = "grid-nivel-item-name", paste0("N", nv, " ", info$name)),
|
| 1868 |
-
span(class = "grid-nivel-item-size",
|
| 1869 |
div(class = "grid-nivel-item-desc", info$desc)
|
| 1870 |
)
|
| 1871 |
})
|
|
@@ -1876,7 +1981,7 @@ ui <- page_fillable(
|
|
| 1876 |
title = "Coming soon",
|
| 1877 |
div(class = "grid-nivel-item-row",
|
| 1878 |
span(class = "grid-nivel-item-name", paste0("N", nv)),
|
| 1879 |
-
span(class = "grid-nivel-item-size", "~
|
| 1880 |
div(class = "grid-nivel-item-desc", "High resolution")))
|
| 1881 |
)
|
| 1882 |
),
|
|
@@ -1907,7 +2012,14 @@ ui <- page_fillable(
|
|
| 1907 |
actionButton("toggle_3d", class = "nav-btn",
|
| 1908 |
`aria-label` = "Toggle 2D / 3D view",
|
| 1909 |
title = "Toggle 2D / 3D",
|
| 1910 |
-
HTML('<i data-lucide="box" style="width:16px;height:16px;stroke-width:2"></i>'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1911 |
),
|
| 1912 |
|
| 1913 |
div(class = "nav-divider"),
|
|
|
|
| 864 |
# --- Lucide icons + JS helpers --------------------------------------------------
|
| 865 |
LUCIDE_JS <- tags$head(
|
| 866 |
tags$script(src = "https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"),
|
| 867 |
+
# Driver.js -- guided spotlight tour (MIT). unpkg matches Lucide's origin so
|
| 868 |
+
# the same CSP allowlist covers it. Drives existing DOM elements; no R logic.
|
| 869 |
+
tags$link(rel = "stylesheet",
|
| 870 |
+
href = "https://unpkg.com/driver.js@1.3.1/dist/driver.css"),
|
| 871 |
+
tags$script(src = "https://unpkg.com/driver.js@1.3.1/dist/driver.js.iife.js"),
|
| 872 |
tags$script(HTML("
|
| 873 |
// gridMenuOpen = is the N4-N8 resolution menu visible (purely visual);
|
| 874 |
// gridActive = is the grid overlay currently shown on the map.
|
|
|
|
| 915 |
Shiny.setInputValue('grid_nivel', '6', { priority: 'event' });
|
| 916 |
}, 300);
|
| 917 |
|
| 918 |
+
// First-visit auto-tour: wait for the map to settle, then run once.
|
| 919 |
+
try {
|
| 920 |
+
if (!localStorage.getItem('depth_tour_seen')) {
|
| 921 |
+
setTimeout(function() { if (window.startTour) window.startTour(); }, 1200);
|
| 922 |
+
}
|
| 923 |
+
} catch (e) {}
|
| 924 |
+
|
| 925 |
// Hook up the native draw buttons so our custom ones can mirror their
|
| 926 |
// active state. Retries until mapgl has mounted the draw control.
|
| 927 |
var navTries = 0;
|
|
|
|
| 967 |
if (tip) tip.style.display = 'none';
|
| 968 |
}
|
| 969 |
|
| 970 |
+
// -- Guided spotlight tour (Driver.js) --------------------------------------
|
| 971 |
+
// Highlights the real controls one step at a time. Auto-runs once on first
|
| 972 |
+
// visit (localStorage flag); the nav '?' button replays it on demand.
|
| 973 |
+
// Some targets live in collapsed panels, so steps open them on enter and
|
| 974 |
+
// restore them on leave; onDestroyed guarantees nothing is left open.
|
| 975 |
+
var _tourLayersOpened = false;
|
| 976 |
+
|
| 977 |
+
function _isLayersOpen() {
|
| 978 |
+
var body = document.getElementById('layer-ctrl-body');
|
| 979 |
+
return !!(body && body.style.display !== 'none');
|
| 980 |
+
}
|
| 981 |
+
function _restoreLayers() {
|
| 982 |
+
if (_tourLayersOpened && _isLayersOpen()) toggleLayerPanel();
|
| 983 |
+
_tourLayersOpened = false;
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
function startTour() {
|
| 987 |
+
if (!window.driver || !window.driver.js) return;
|
| 988 |
+
var driver = window.driver.js.driver;
|
| 989 |
+
|
| 990 |
+
var d = driver({
|
| 991 |
+
showProgress: true,
|
| 992 |
+
allowClose: true,
|
| 993 |
+
overlayOpacity: 0.6,
|
| 994 |
+
nextBtnText: 'Next',
|
| 995 |
+
prevBtnText: 'Back',
|
| 996 |
+
doneBtnText: 'Done',
|
| 997 |
+
onDestroyed: function() {
|
| 998 |
+
_restoreLayers();
|
| 999 |
+
try { localStorage.setItem('depth_tour_seen', '1'); } catch (e) {}
|
| 1000 |
+
},
|
| 1001 |
+
steps: [
|
| 1002 |
+
{ popover: {
|
| 1003 |
+
title: 'Welcome to DEPTH',
|
| 1004 |
+
description: 'An interactive map of migration risk in Mexico. This quick tour points out what each control does -- about 60 seconds. Use Next, or close it any time.'
|
| 1005 |
+
}},
|
| 1006 |
+
{ element: '#nav-search-wrap', popover: {
|
| 1007 |
+
title: 'Search',
|
| 1008 |
+
description: 'Jump to a place, municipality, or exact coordinates (e.g. \"19.43,-99.13\").',
|
| 1009 |
+
side: 'bottom', align: 'start'
|
| 1010 |
+
}},
|
| 1011 |
+
{ element: '#home_btn', popover: {
|
| 1012 |
+
title: 'Overview',
|
| 1013 |
+
description: 'Reset the map back to the full view of Mexico at any point.',
|
| 1014 |
+
side: 'bottom', align: 'end'
|
| 1015 |
+
}},
|
| 1016 |
+
{ element: '#nav-tools-select', popover: {
|
| 1017 |
+
title: 'Select an area to analyse',
|
| 1018 |
+
description: 'Draw a lasso, box, circle or line -- or click a single municipality. The sidebar then summarises every indicator inside your selection.',
|
| 1019 |
+
side: 'bottom', align: 'center'
|
| 1020 |
+
}},
|
| 1021 |
+
{ element: '#btn_state_filter', popover: {
|
| 1022 |
+
title: 'Filter by state',
|
| 1023 |
+
description: 'Restrict the analysis to one Mexican state.',
|
| 1024 |
+
side: 'bottom', align: 'center'
|
| 1025 |
+
}},
|
| 1026 |
+
{ element: '#grid-icon-btn', popover: {
|
| 1027 |
+
title: 'Spatial grid',
|
| 1028 |
+
description: 'Overlay an equal-cell grid and switch resolution from N4 (national overview) to N7 (local detail). Grid colours follow the active layer.',
|
| 1029 |
+
side: 'bottom', align: 'center'
|
| 1030 |
+
}},
|
| 1031 |
+
{ element: '.style-btn-wrap', popover: {
|
| 1032 |
+
title: 'Basemap',
|
| 1033 |
+
description: 'Switch the background map between light, dark, and a labelled style.',
|
| 1034 |
+
side: 'bottom', align: 'center'
|
| 1035 |
+
}},
|
| 1036 |
+
{ element: '#toggle_3d', popover: {
|
| 1037 |
+
title: '2D / 3D',
|
| 1038 |
+
description: 'Tilt the map into a 3D perspective view.',
|
| 1039 |
+
side: 'bottom', align: 'end'
|
| 1040 |
+
}},
|
| 1041 |
+
{ element: '#layer-ctrl-body', popover: {
|
| 1042 |
+
title: 'Data layers',
|
| 1043 |
+
description: 'Turn indicators on and off here. They are grouped into Hazards, Exposure and Vulnerability. Each layer has a short description and its own legend.',
|
| 1044 |
+
side: 'right', align: 'start'
|
| 1045 |
+
},
|
| 1046 |
+
onHighlightStarted: function() {
|
| 1047 |
+
if (!_isLayersOpen()) { toggleLayerPanel(); _tourLayersOpened = true; }
|
| 1048 |
+
},
|
| 1049 |
+
onDeselected: function() { _restoreLayers(); }
|
| 1050 |
+
},
|
| 1051 |
+
{ element: '#panel-tmri', popover: {
|
| 1052 |
+
title: 'Analysis sidebar',
|
| 1053 |
+
description: 'Live totals, charts over time, and breakdowns for whatever you have selected -- plus a button to download the selection as an .xlsx spreadsheet.',
|
| 1054 |
+
side: 'left', align: 'start'
|
| 1055 |
+
}}
|
| 1056 |
+
]
|
| 1057 |
+
});
|
| 1058 |
+
|
| 1059 |
+
d.drive();
|
| 1060 |
+
}
|
| 1061 |
+
window.startTour = startTour;
|
| 1062 |
+
|
| 1063 |
function clearDrawing() {
|
| 1064 |
var btn = document.getElementById('clear_btn');
|
| 1065 |
if (btn) btn.click();
|
|
|
|
| 1967 |
class = "grid-nivel-item",
|
| 1968 |
`data-nivel` = nv,
|
| 1969 |
onclick = paste0("setGridNivel('", nv, "')"),
|
| 1970 |
+
`aria-label` = paste0("Set grid to ", info$name, ", ", info$size, ", ", info$desc),
|
| 1971 |
div(class = "grid-nivel-item-row",
|
| 1972 |
span(class = "grid-nivel-item-name", paste0("N", nv, " ", info$name)),
|
| 1973 |
+
span(class = "grid-nivel-item-size", info$size)),
|
| 1974 |
div(class = "grid-nivel-item-desc", info$desc)
|
| 1975 |
)
|
| 1976 |
})
|
|
|
|
| 1981 |
title = "Coming soon",
|
| 1982 |
div(class = "grid-nivel-item-row",
|
| 1983 |
span(class = "grid-nivel-item-name", paste0("N", nv)),
|
| 1984 |
+
span(class = "grid-nivel-item-size", "~1.3 km cell length")),
|
| 1985 |
div(class = "grid-nivel-item-desc", "High resolution")))
|
| 1986 |
)
|
| 1987 |
),
|
|
|
|
| 2012 |
actionButton("toggle_3d", class = "nav-btn",
|
| 2013 |
`aria-label` = "Toggle 2D / 3D view",
|
| 2014 |
title = "Toggle 2D / 3D",
|
| 2015 |
+
HTML('<i data-lucide="box" style="width:16px;height:16px;stroke-width:2"></i>')),
|
| 2016 |
+
|
| 2017 |
+
# Guided tour replay
|
| 2018 |
+
tags$button(class = "nav-btn", type = "button",
|
| 2019 |
+
onclick = "startTour()",
|
| 2020 |
+
`aria-label` = "Take a guided tour",
|
| 2021 |
+
title = "Take a guided tour",
|
| 2022 |
+
HTML('<i data-lucide="circle-help" style="width:16px;height:16px;stroke-width:2"></i>'))
|
| 2023 |
),
|
| 2024 |
|
| 2025 |
div(class = "nav-divider"),
|
styles.css
CHANGED
|
@@ -2389,3 +2389,43 @@ button.mapbox-gl-draw_uncombine { display: none !important; }
|
|
| 2389 |
color: var(--c-text);
|
| 2390 |
letter-spacing: -0.01em;
|
| 2391 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2389 |
color: var(--c-text);
|
| 2390 |
letter-spacing: -0.01em;
|
| 2391 |
}
|
| 2392 |
+
|
| 2393 |
+
/* --- Guided tour (Driver.js) -------------------------------------------------
|
| 2394 |
+
Theme the spotlight popover to the DEPTH brand. Driver.js sets its own high
|
| 2395 |
+
z-index on the overlay/popover; we keep it above the maplibre canvas. */
|
| 2396 |
+
.driver-popover.depth-popover,
|
| 2397 |
+
.driver-popover {
|
| 2398 |
+
background: #fff;
|
| 2399 |
+
color: var(--c-text);
|
| 2400 |
+
border: 1px solid var(--c-border-str);
|
| 2401 |
+
border-radius: 10px;
|
| 2402 |
+
box-shadow: 0 10px 30px rgba(3, 36, 37, 0.18);
|
| 2403 |
+
max-width: 320px;
|
| 2404 |
+
}
|
| 2405 |
+
.driver-popover .driver-popover-title {
|
| 2406 |
+
font-weight: 700;
|
| 2407 |
+
color: var(--c-text);
|
| 2408 |
+
}
|
| 2409 |
+
.driver-popover .driver-popover-description {
|
| 2410 |
+
color: var(--c-text-mid);
|
| 2411 |
+
line-height: 1.45;
|
| 2412 |
+
}
|
| 2413 |
+
.driver-popover .driver-popover-progress-text {
|
| 2414 |
+
color: var(--c-text-dim);
|
| 2415 |
+
}
|
| 2416 |
+
.driver-popover .driver-popover-navigation-btns button {
|
| 2417 |
+
background: var(--c-accent);
|
| 2418 |
+
color: #fff;
|
| 2419 |
+
text-shadow: none;
|
| 2420 |
+
border: none;
|
| 2421 |
+
border-radius: 6px;
|
| 2422 |
+
font-weight: 600;
|
| 2423 |
+
}
|
| 2424 |
+
.driver-popover .driver-popover-navigation-btns button.driver-popover-prev-btn {
|
| 2425 |
+
background: transparent;
|
| 2426 |
+
color: var(--c-text-mid);
|
| 2427 |
+
border: 1px solid var(--c-border);
|
| 2428 |
+
}
|
| 2429 |
+
.driver-popover .driver-popover-close-btn {
|
| 2430 |
+
color: var(--c-text-dim);
|
| 2431 |
+
}
|