edgarodriguez commited on
Commit
aa36d68
·
verified ·
1 Parent(s): a811646

Upload 2 files

Browse files
Files changed (2) hide show
  1. depth_interactive_map_alpha.R +168 -9
  2. styles.css +3 -0
depth_interactive_map_alpha.R CHANGED
@@ -107,12 +107,44 @@ GRAVES_PAL <- c("#fee5d9", "#fcae91", "#fb6a4a",
107
  "#de2d26", "#a50f15", "#67000d") # Reds 6
108
  INFRA_PAL <- c("#ffffe5", "#f7fcb9", "#d9f0a3",
109
  "#addd8e", "#78c679", "#238443") # YlGn 6
110
- OCVED_PAL <- c("#feedde", "#fdbe85", "#fd8d3c",
111
- "#e6550d", "#a63603", "#7f2704") # Oranges 6
112
  GED_PAL <- c("#f1eef6", "#d0d1e6", "#a6bddb",
113
  "#67a9cf", "#2b8cbe", "#045a8d") # PuBu 6
114
  FOSAS_PAL <- c("#ffffd4", "#fee391", "#fec44f",
115
  "#fe9929", "#d95f0e", "#993404") # YlOrBr 6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  # Infrastructure point categories -- grouped from the raw "category" field
118
  # (Shelter / restaurant / Government / lodging / Legal/NGO / Food / charity /
@@ -149,13 +181,16 @@ GRID_LAYER_CFG <- list(
149
  "fosas-choro" = "fosas",
150
  "remesas-choro" = "remesas",
151
  "pam-choro" = "pam",
 
 
152
  "migrants-pts" = "migrants",
153
  "graves-pts" = "graves",
154
  "infra-pts" = "infra",
155
  "ocved-pts" = "ocved",
156
  "ged-pts" = "ged"
157
  )
158
- NON_GRID_LAYERS <- c("envipe-state", "routes-lines", "viaferrea-lines")
 
159
 
160
  ENVIPE_COLS <- list(
161
  "insecurity" = list(col = "insecurity_perc", breaks = c(40, 55, 65, 75, 85)),
@@ -300,6 +335,49 @@ WEB_SQL_TEMPLATES <- list(
300
  FROM dim_pam_mun GROUP BY CVEGEO) p
301
  ON c.CVEGEO = p.CVEGEO",
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  src_fosas =
304
  "SELECT c.municipio, c.entidad, c.CVEGEO,
305
  COALESCE(CAST(f.fosas_total AS INTEGER), 0) AS fosas_total,
@@ -498,9 +576,12 @@ web_sql_grid_unified <- function(niv, has_fosas, limit = NULL, simplify_tol = 0.
498
  CAST(c.secuestro AS DOUBLE) AS secuestro,
499
  CAST(c.trafico_menores AS DOUBLE) AS trafico_menores,
500
  CAST(c.trata_personas AS DOUBLE) AS trata_personas,
 
501
  COALESCE(CAST(d.desap_rate AS DOUBLE), 0) AS desap_per_100k{fosas_cols},
502
  COALESCE(r.giim_mun, 'No aplica') AS giim_mun,
503
  COALESCE(CAST(p.pam_total AS INTEGER), 0) AS pam_n,
 
 
504
  COALESCE(pts.migrants_n, 0) AS migrants_n,
505
  COALESCE(pts.graves_n, 0) AS graves_n,
506
  COALESCE(pts.ocved_n, 0) AS ocved_n,
@@ -511,6 +592,7 @@ web_sql_grid_unified <- function(niv, has_fosas, limit = NULL, simplify_tol = 0.
511
  LEFT JOIN {desap_sub} d ON g.CVEGEO = d.CVEGEO{fosas_join}
512
  LEFT JOIN dim_remesas_mun r ON g.CVEGEO = r.CVEGEO
513
  LEFT JOIN {pam_sub} p ON g.CVEGEO = p.CVEGEO
 
514
  LEFT JOIN pts_src pts ON g.codigo = pts.codigo{limit_clause}"
515
  )
516
  }
@@ -580,7 +662,8 @@ ddb_minmax <- function(con, tbl, col, filter_zero = TRUE) {
580
  crime = lapply(CRIME_COLS, function(info)
581
  qbq("crime_mun", info$col, length(CRIME_STEP_PAL))),
582
  desap = qbq("dim_desap_mun", "final_desap_nl_per_100k", length(DESAP_PAL)),
583
- pam = qbq("dim_pam_mun", "pam_n", length(PAM_PAL))
 
584
  ),
585
  # Actual min/max per layer's primary column -- powers the legend labels.
586
  # Dynamic sub-types (crime/envipe/fosas) cache the DEFAULT sub-type only;
@@ -589,6 +672,7 @@ ddb_minmax <- function(con, tbl, col, filter_zero = TRUE) {
589
  crime = ddb_minmax(con, "crime_mun", "homicidio", TRUE),
590
  desap = ddb_minmax(con, "dim_desap_mun", "final_desap_nl_per_100k", TRUE),
591
  pam = ddb_minmax(con, "dim_pam_mun", "pam_n", TRUE),
 
592
  fosas = ddb_minmax(con, "dim_fosas_mun", "fosas_total", TRUE),
593
  envipe = ddb_minmax(con, "state_polygons", "perception_insecurity_perc", FALSE)
594
  )
@@ -685,7 +769,8 @@ WEB_SPEC <- list(
685
  migrants = MIGRANTS_PAL, graves = GRAVES_PAL, infra = INFRA_PAL,
686
  ocved = OCVED_PAL, ged = GED_PAL,
687
  giim = GIIM_COLORS, bar_ramp = BAR_RAMP,
688
- infra_cats = INFRA_CATS
 
689
  ),
690
 
691
  crime_cols = CRIME_COLS,
@@ -718,6 +803,9 @@ WEB_SPEC <- list(
718
  list(id = "pam-choro", type = "fill", src = "src_pam",
719
  geom = "polygon", color = "pam_qstep", value = "pam_n",
720
  opacity = 0.7, visible = FALSE),
 
 
 
721
  list(id = "fosas-choro", type = "fill", src = "src_fosas",
722
  geom = "polygon", color = "fosas_step", value = "fosas_total",
723
  opacity = 0.65, visible = FALSE, requires = "dim_fosas_mun"),
@@ -802,7 +890,13 @@ WEB_SPEC <- list(
802
  label = "Railway network",
803
  tooltip = "Railway lines used by migrants, including freight routes known as 'La Bestia'.",
804
  legend = list(type = "line", color = "#9E2A3A", label = "Railway", dashed = TRUE)),
805
- list(kind = "soon", label = "Foreign population")
 
 
 
 
 
 
806
  )),
807
  list(id = "vulnerability", label = "Vulnerability", open = FALSE, items = list(
808
  list(kind = "layer", value = "infra-pts",
@@ -1233,6 +1327,29 @@ LUCIDE_JS <- tags$head(
1233
  }
1234
  }
1235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1236
  // -- Side panel toggle (sigma / network / sparkles tab rail) ---------------
1237
  function togglePanel(name) {
1238
  var panel = document.getElementById('panel-' + name);
@@ -2027,6 +2144,15 @@ ui <- page_fillable(
2027
  title = "Toggle 2D / 3D",
2028
  HTML('<i data-lucide="box" style="width:16px;height:16px;stroke-width:2"></i>')),
2029
 
 
 
 
 
 
 
 
 
 
2030
  # Guided tour replay
2031
  tags$button(class = "nav-btn", type = "button",
2032
  onclick = "startTour()",
@@ -2305,6 +2431,7 @@ server <- function(input, output, session) {
2305
  state = FALSE, routes = FALSE, via = FALSE,
2306
  migrants = FALSE, graves = FALSE, infra = FALSE,
2307
  ocved = FALSE, ged = FALSE,
 
2308
  grid4 = FALSE, grid5 = FALSE, grid6 = FALSE
2309
  )
2310
 
@@ -2417,14 +2544,30 @@ server <- function(input, output, session) {
2417
  count_step_expr("pam_n", breaks, PAM_PAL)
2418
  }
2419
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2420
  # Return the fill-color expression for a given original layer ID
2421
- grid_color_expr <- function(orig_lid, crime_type, fosas_type = "fosas") {
 
2422
  switch(orig_lid,
2423
  "crime-choro" = crime_qstep_expr(crime_type),
2424
  "desap-choro" = desap_qstep_expr(),
2425
  "fosas-choro" = fosas_step_expr(fosas_type),
2426
  "remesas-choro" = giim_match_expr(),
2427
  "pam-choro" = pam_qstep_expr(),
 
 
2428
  "migrants-pts" = count_step_expr("migrants_n", c(1, 5, 20, 50, 100), MIGRANTS_PAL),
2429
  "graves-pts" = count_step_expr("graves_n", c(1, 2, 5, 15, 30), GRAVES_PAL),
2430
  "infra-pts" = count_step_expr("infra_n", c(1, 3, 8, 20, 50), INFRA_PAL),
@@ -2442,6 +2585,7 @@ server <- function(input, output, session) {
2442
  "desap-choro" = isTRUE(lazy_added$desap),
2443
  "remesas-choro" = isTRUE(lazy_added$remesas),
2444
  "pam-choro" = isTRUE(lazy_added$pam),
 
2445
  "fosas-choro" = isTRUE(lazy_added$fosas),
2446
  "envipe-state" = isTRUE(lazy_added$state),
2447
  "routes-lines" = isTRUE(lazy_added$routes),
@@ -2520,7 +2664,7 @@ server <- function(input, output, session) {
2520
  show <- grid_on && orig_lid %in% active && niv == nivel
2521
  prx <- prx |> set_paint_property(grid_id, "fill-opacity", if (show) 0.65 else 0)
2522
  if (show) {
2523
- expr <- grid_color_expr(orig_lid, crime_type, fosas_type)
2524
  if (!is.null(expr))
2525
  prx <- prx |> set_paint_property(grid_id, "fill-color", expr)
2526
  }
@@ -2765,6 +2909,15 @@ server <- function(input, output, session) {
2765
  fill_opacity = 0, tooltip = grid_tooltip) |>
2766
  add_fill_layer(id = paste0("grid-ged-n", niv), source = src,
2767
  fill_color = count_step_expr("ged_n", c(1, 3, 10, 30, 80), GED_PAL),
 
 
 
 
 
 
 
 
 
2768
  fill_opacity = 0, tooltip = grid_tooltip)
2769
  m
2770
  }
@@ -3106,6 +3259,12 @@ server <- function(input, output, session) {
3106
  fill_color = pam_qstep_expr(), fill_opacity = 0.7,
3107
  tooltip = list("concat", "<b>", list("get","municipio"), "</b>",
3108
  "<br>Irregular migration events: ", list("to-string", list("get","pam_n")))))
 
 
 
 
 
 
3109
  if (!grid_on && "fosas-choro" %in% active && has_fosas_tbl)
3110
  lazy_add_fill("fosas", "src_fosas", "fosas-choro", function(prx, d)
3111
  prx |> add_fill_layer("fosas-choro", source = d,
@@ -3117,7 +3276,7 @@ server <- function(input, output, session) {
3117
  lazy_add_fill("state", "src_state", "envipe-state", function(prx, d)
3118
  prx |> add_fill_layer("envipe-state", source = d,
3119
  fill_color = step_expr("insecurity_perc", c(40,55,65,75,85), ENVIPE_PAL),
3120
- fill_opacity = 0.6,
3121
  tooltip = list("concat", "<b>", list("get","desc_entidad"), "</b>",
3122
  "<br>Insecurity: ", list("to-string", list("get","insecurity_perc")), "%",
3123
  "<br>Dark figures: ", list("to-string", list("get","dark_figures_perc")), "%")))
 
107
  "#de2d26", "#a50f15", "#67000d") # Reds 6
108
  INFRA_PAL <- c("#ffffe5", "#f7fcb9", "#d9f0a3",
109
  "#addd8e", "#78c679", "#238443") # YlGn 6
110
+ OCVED_PAL <- c("#f1eef6", "#d0d1e6", "#a6bddb",
111
+ "#67a9cf", "#2b8cbe", "#045a8d") # PuBu 6
112
  GED_PAL <- c("#f1eef6", "#d0d1e6", "#a6bddb",
113
  "#67a9cf", "#2b8cbe", "#045a8d") # PuBu 6
114
  FOSAS_PAL <- c("#ffffd4", "#fee391", "#fec44f",
115
  "#fe9929", "#d95f0e", "#993404") # YlOrBr 6
116
+ POP_PAL <- c("#f7fcf0", "#e0f3db", "#ccebc5",
117
+ "#a8ddb5", "#7bccc4", "#4eb3d3",
118
+ "#2b8cbe") # GnBu 7 (Exposure)
119
+ PATP_PAL <- c("#fff7f3", "#fde0dd", "#fcc5c0",
120
+ "#fa9fb5", "#f768a1", "#c51b8a") # RdPu 6 (Vulnerability)
121
+ POBREZA_PAL <- c("#feebe2", "#fbb4b9", "#f768a1",
122
+ "#c51b8a", "#7a0177", "#49006a") # RdPu 6 (deep, poverty)
123
+
124
+ # Categorical palettes for the two non-numeric patp sub-types. Stable colour-blind
125
+ # safe Set2-style hues; keys must match the truncated label list in setPATPType().
126
+ PATP_RURAL_PROBLEM_PAL <- c(
127
+ "Sociales y económicos" = "#66c2a5",
128
+ "Carencia o deficiencia en servicios básicos" = "#fc8d62",
129
+ "Caminos, transportes y telecomunicaciones" = "#8da0cb",
130
+ "Inseguridad, delincuencia y adicciones" = "#e78ac3",
131
+ "Carencia o deficiencia en la infraestructura educativa y de salud"= "#a6d854",
132
+ "Municipios sin localidades objetivo" = "#ffd92f",
133
+ "Sin problema" = "#e5c494",
134
+ "Falta de información / Otro problema" = "#b3b3b3",
135
+ "Riesgos ambientales" = "#1f78b4",
136
+ "Problema no definido" = "#cab2d6"
137
+ )
138
+ PATP_DELITO_PAL <- c(
139
+ "El patrimonio" = "#e41a1c",
140
+ "otros bienes" = "#377eb8",
141
+ "La vida y la Integridad corporal" = "#4daf4a",
142
+ "múltiples bienes" = "#984ea3",
143
+ "La familia" = "#ff7f00",
144
+ "Sin delito" = "#a6cee3",
145
+ "La libertad y la seguridad sexual" = "#b15928",
146
+ "Libertad personal" = "#6a3d9a"
147
+ )
148
 
149
  # Infrastructure point categories -- grouped from the raw "category" field
150
  # (Shelter / restaurant / Government / lodging / Legal/NGO / Food / charity /
 
181
  "fosas-choro" = "fosas",
182
  "remesas-choro" = "remesas",
183
  "pam-choro" = "pam",
184
+ "envipe-state" = "envipe", # ENVIPE now also paints per grid cell
185
+ "pop-choro" = "pop",
186
  "migrants-pts" = "migrants",
187
  "graves-pts" = "graves",
188
  "infra-pts" = "infra",
189
  "ocved-pts" = "ocved",
190
  "ged-pts" = "ged"
191
  )
192
+ # envipe-state is no longer a NON_GRID_LAYER -- it now appears on the grid too.
193
+ NON_GRID_LAYERS <- c("routes-lines", "viaferrea-lines")
194
 
195
  ENVIPE_COLS <- list(
196
  "insecurity" = list(col = "insecurity_perc", breaks = c(40, 55, 65, 75, 85)),
 
335
  FROM dim_pam_mun GROUP BY CVEGEO) p
336
  ON c.CVEGEO = p.CVEGEO",
337
 
338
+ src_population =
339
+ "SELECT municipio, entidad, CVEGEO,
340
+ CAST(poblacion AS DOUBLE) AS poblacion,
341
+ wkt
342
+ FROM crime_mun
343
+ WHERE poblacion IS NOT NULL",
344
+
345
+ # patp: dim_patp_mun stores numeric values as VARCHAR; TRY_CAST converts
346
+ # the 'n.d.' sentinels (and other non-numeric strings) to NULL.
347
+ src_patp =
348
+ "SELECT c.municipio, c.entidad, c.CVEGEO,
349
+ TRY_CAST(p.t_part AS DOUBLE) AS t_part,
350
+ TRY_CAST(p.t_ocup AS DOUBLE) AS t_ocup,
351
+ TRY_CAST(p.pr AS DOUBLE) AS pr,
352
+ CAST(p.ind_conec AS DOUBLE) AS ind_conec,
353
+ p.pr_pred_loc_rur,
354
+ p.delitos_fc_b_j_a,
355
+ c.wkt
356
+ FROM crime_mun c
357
+ LEFT JOIN dim_patp_mun p ON c.CVEGEO = p.CVEGEO",
358
+
359
+ src_pobreza =
360
+ "SELECT c.municipio, c.entidad, c.CVEGEO,
361
+ CAST(pob.pobreza_pct AS DOUBLE) AS pobreza_pct,
362
+ CAST(pob.pobreza_ext_pct AS DOUBLE) AS pobreza_ext_pct,
363
+ CAST(pob.pobreza_mod_pct AS DOUBLE) AS pobreza_mod_pct,
364
+ CAST(pob.vul_carencia_pct AS DOUBLE) AS vul_carencia_pct,
365
+ CAST(pob.vul_ingreso_pct AS DOUBLE) AS vul_ingreso_pct,
366
+ CAST(pob.no_pobre_no_vul_pct AS DOUBLE) AS no_pobre_no_vul_pct,
367
+ CAST(pob.rezago_edu_pct AS DOUBLE) AS rezago_edu_pct,
368
+ CAST(pob.caren_salud_pct AS DOUBLE) AS caren_salud_pct,
369
+ CAST(pob.caren_seg_social_pct AS DOUBLE) AS caren_seg_social_pct,
370
+ CAST(pob.caren_vivienda_pct AS DOUBLE) AS caren_vivienda_pct,
371
+ CAST(pob.caren_serv_basicos_pct AS DOUBLE) AS caren_serv_basicos_pct,
372
+ CAST(pob.caren_alimentacion_pct AS DOUBLE) AS caren_alimentacion_pct,
373
+ CAST(pob.min_1_carencia_pct AS DOUBLE) AS min_1_carencia_pct,
374
+ CAST(pob.min_3_carencias_pct AS DOUBLE) AS min_3_carencias_pct,
375
+ CAST(pob.ing_inf_lp_pct AS DOUBLE) AS ing_inf_lp_pct,
376
+ CAST(pob.ing_inf_lpe_pct AS DOUBLE) AS ing_inf_lpe_pct,
377
+ c.wkt
378
+ FROM crime_mun c
379
+ LEFT JOIN dim_pobreza_mun pob ON c.CVEGEO = pob.CVEGEO",
380
+
381
  src_fosas =
382
  "SELECT c.municipio, c.entidad, c.CVEGEO,
383
  COALESCE(CAST(f.fosas_total AS INTEGER), 0) AS fosas_total,
 
576
  CAST(c.secuestro AS DOUBLE) AS secuestro,
577
  CAST(c.trafico_menores AS DOUBLE) AS trafico_menores,
578
  CAST(c.trata_personas AS DOUBLE) AS trata_personas,
579
+ CAST(c.poblacion AS DOUBLE) AS poblacion,
580
  COALESCE(CAST(d.desap_rate AS DOUBLE), 0) AS desap_per_100k{fosas_cols},
581
  COALESCE(r.giim_mun, 'No aplica') AS giim_mun,
582
  COALESCE(CAST(p.pam_total AS INTEGER), 0) AS pam_n,
583
+ CAST(s.perception_insecurity_perc AS DOUBLE) AS insecurity_perc,
584
+ CAST(s.dark_figures_perc AS DOUBLE) AS dark_figures_perc,
585
  COALESCE(pts.migrants_n, 0) AS migrants_n,
586
  COALESCE(pts.graves_n, 0) AS graves_n,
587
  COALESCE(pts.ocved_n, 0) AS ocved_n,
 
592
  LEFT JOIN {desap_sub} d ON g.CVEGEO = d.CVEGEO{fosas_join}
593
  LEFT JOIN dim_remesas_mun r ON g.CVEGEO = r.CVEGEO
594
  LEFT JOIN {pam_sub} p ON g.CVEGEO = p.CVEGEO
595
+ LEFT JOIN state_polygons s ON s.CVE_ENT = SUBSTR(g.CVEGEO, 1, 2)
596
  LEFT JOIN pts_src pts ON g.codigo = pts.codigo{limit_clause}"
597
  )
598
  }
 
662
  crime = lapply(CRIME_COLS, function(info)
663
  qbq("crime_mun", info$col, length(CRIME_STEP_PAL))),
664
  desap = qbq("dim_desap_mun", "final_desap_nl_per_100k", length(DESAP_PAL)),
665
+ pam = qbq("dim_pam_mun", "pam_n", length(PAM_PAL)),
666
+ pop = qbq("crime_mun", "poblacion", length(POP_PAL))
667
  ),
668
  # Actual min/max per layer's primary column -- powers the legend labels.
669
  # Dynamic sub-types (crime/envipe/fosas) cache the DEFAULT sub-type only;
 
672
  crime = ddb_minmax(con, "crime_mun", "homicidio", TRUE),
673
  desap = ddb_minmax(con, "dim_desap_mun", "final_desap_nl_per_100k", TRUE),
674
  pam = ddb_minmax(con, "dim_pam_mun", "pam_n", TRUE),
675
+ pop = ddb_minmax(con, "crime_mun", "poblacion", TRUE),
676
  fosas = ddb_minmax(con, "dim_fosas_mun", "fosas_total", TRUE),
677
  envipe = ddb_minmax(con, "state_polygons", "perception_insecurity_perc", FALSE)
678
  )
 
769
  migrants = MIGRANTS_PAL, graves = GRAVES_PAL, infra = INFRA_PAL,
770
  ocved = OCVED_PAL, ged = GED_PAL,
771
  giim = GIIM_COLORS, bar_ramp = BAR_RAMP,
772
+ infra_cats = INFRA_CATS,
773
+ pop = POP_PAL
774
  ),
775
 
776
  crime_cols = CRIME_COLS,
 
803
  list(id = "pam-choro", type = "fill", src = "src_pam",
804
  geom = "polygon", color = "pam_qstep", value = "pam_n",
805
  opacity = 0.7, visible = FALSE),
806
+ list(id = "pop-choro", type = "fill", src = "src_population",
807
+ geom = "polygon", color = "pop_qstep", value = "poblacion",
808
+ opacity = 0.7, visible = FALSE),
809
  list(id = "fosas-choro", type = "fill", src = "src_fosas",
810
  geom = "polygon", color = "fosas_step", value = "fosas_total",
811
  opacity = 0.65, visible = FALSE, requires = "dim_fosas_mun"),
 
890
  label = "Railway network",
891
  tooltip = "Railway lines used by migrants, including freight routes known as 'La Bestia'.",
892
  legend = list(type = "line", color = "#9E2A3A", label = "Railway", dashed = TRUE)),
893
+ list(kind = "layer", value = "pop-choro",
894
+ label = "Municipal population (CONAPO 2020)",
895
+ tooltip = "Estimated total population per municipality (CONAPO 2020 projection). Higher values shown in deeper blue-green.",
896
+ legend = list(type = "ramp", pal = "pop", unit = " people")),
897
+ list(kind = "soon", label = "Foreign population"),
898
+ list(kind = "soon", label = "PATP indicators (t_ocup, t_part, pr, ind_conec, pr_pred_loc_rur, delitos_fc_b_j_a)"),
899
+ list(kind = "soon", label = "CONEVAL pobreza indicators (16 %-cols)")
900
  )),
901
  list(id = "vulnerability", label = "Vulnerability", open = FALSE, items = list(
902
  list(kind = "layer", value = "infra-pts",
 
1327
  }
1328
  }
1329
 
1330
+ // -- Map hover-tooltip on/off ----------------------------------------------
1331
+ // Hides the maplibre popup that mapgl attaches to layers with tooltip = ...
1332
+ // Visual-only: layers stay interactive (click, draw, etc.); only the hover
1333
+ // text is suppressed via CSS when 'tooltips-off' is set on <body>.
1334
+ function toggleMapTooltips() {
1335
+ var on = !document.body.classList.contains('tooltips-off');
1336
+ document.body.classList.toggle('tooltips-off', on);
1337
+ var btn = document.getElementById('tooltip-toggle-btn');
1338
+ if (btn) {
1339
+ btn.classList.toggle('active', !on);
1340
+ btn.setAttribute('aria-pressed', String(!on));
1341
+ btn.setAttribute('title', on ? 'Tooltips off (click to enable)' : 'Tooltips on (click to disable)');
1342
+ var icon = btn.querySelector('i');
1343
+ if (icon) icon.setAttribute('data-lucide', on ? 'message-square-off' : 'message-square');
1344
+ if (window.lucide) lucide.createIcons();
1345
+ }
1346
+ // Force-hide any popup that's currently open
1347
+ document.querySelectorAll('.maplibregl-popup').forEach(function(p) {
1348
+ if (on) p.style.display = 'none';
1349
+ else p.style.display = '';
1350
+ });
1351
+ }
1352
+
1353
  // -- Side panel toggle (sigma / network / sparkles tab rail) ---------------
1354
  function togglePanel(name) {
1355
  var panel = document.getElementById('panel-' + name);
 
2144
  title = "Toggle 2D / 3D",
2145
  HTML('<i data-lucide="box" style="width:16px;height:16px;stroke-width:2"></i>')),
2146
 
2147
+ # Tooltip on/off toggle -- hides the map hover popup when disabled
2148
+ tags$button(id = "tooltip-toggle-btn", class = "nav-btn active",
2149
+ type = "button",
2150
+ onclick = "toggleMapTooltips()",
2151
+ `aria-label` = "Toggle map hover tooltips",
2152
+ `aria-pressed` = "true",
2153
+ title = "Tooltips on (click to disable)",
2154
+ HTML('<i data-lucide="message-square" style="width:16px;height:16px;stroke-width:2" aria-hidden="true"></i>')),
2155
+
2156
  # Guided tour replay
2157
  tags$button(class = "nav-btn", type = "button",
2158
  onclick = "startTour()",
 
2431
  state = FALSE, routes = FALSE, via = FALSE,
2432
  migrants = FALSE, graves = FALSE, infra = FALSE,
2433
  ocved = FALSE, ged = FALSE,
2434
+ pop = FALSE,
2435
  grid4 = FALSE, grid5 = FALSE, grid6 = FALSE
2436
  )
2437
 
 
2544
  count_step_expr("pam_n", breaks, PAM_PAL)
2545
  }
2546
 
2547
+ pop_qstep_expr <- function() {
2548
+ breaks <- qb$pop %||% c(5000, 25000, 100000, 250000, 500000, 1000000)
2549
+ step_expr("poblacion", breaks, POP_PAL)
2550
+ }
2551
+
2552
+ # Envipe on the GRID: each grid cell carries its containing state's
2553
+ # insecurity_perc / dark_figures_perc (joined via SUBSTR(CVEGEO, 1, 2) on the
2554
+ # grid query). The sub-type radio swaps the column the step expression reads.
2555
+ envipe_grid_expr <- function(envipe_type = "insecurity") {
2556
+ info <- ENVIPE_COLS[[envipe_type]] %||% ENVIPE_COLS[["insecurity"]]
2557
+ step_expr(info$col, info$breaks, ENVIPE_PAL)
2558
+ }
2559
+
2560
  # Return the fill-color expression for a given original layer ID
2561
+ grid_color_expr <- function(orig_lid, crime_type, fosas_type = "fosas",
2562
+ envipe_type = "insecurity") {
2563
  switch(orig_lid,
2564
  "crime-choro" = crime_qstep_expr(crime_type),
2565
  "desap-choro" = desap_qstep_expr(),
2566
  "fosas-choro" = fosas_step_expr(fosas_type),
2567
  "remesas-choro" = giim_match_expr(),
2568
  "pam-choro" = pam_qstep_expr(),
2569
+ "pop-choro" = pop_qstep_expr(),
2570
+ "envipe-state" = envipe_grid_expr(envipe_type),
2571
  "migrants-pts" = count_step_expr("migrants_n", c(1, 5, 20, 50, 100), MIGRANTS_PAL),
2572
  "graves-pts" = count_step_expr("graves_n", c(1, 2, 5, 15, 30), GRAVES_PAL),
2573
  "infra-pts" = count_step_expr("infra_n", c(1, 3, 8, 20, 50), INFRA_PAL),
 
2585
  "desap-choro" = isTRUE(lazy_added$desap),
2586
  "remesas-choro" = isTRUE(lazy_added$remesas),
2587
  "pam-choro" = isTRUE(lazy_added$pam),
2588
+ "pop-choro" = isTRUE(lazy_added$pop),
2589
  "fosas-choro" = isTRUE(lazy_added$fosas),
2590
  "envipe-state" = isTRUE(lazy_added$state),
2591
  "routes-lines" = isTRUE(lazy_added$routes),
 
2664
  show <- grid_on && orig_lid %in% active && niv == nivel
2665
  prx <- prx |> set_paint_property(grid_id, "fill-opacity", if (show) 0.65 else 0)
2666
  if (show) {
2667
+ expr <- grid_color_expr(orig_lid, crime_type, fosas_type, envipe_type)
2668
  if (!is.null(expr))
2669
  prx <- prx |> set_paint_property(grid_id, "fill-color", expr)
2670
  }
 
2909
  fill_opacity = 0, tooltip = grid_tooltip) |>
2910
  add_fill_layer(id = paste0("grid-ged-n", niv), source = src,
2911
  fill_color = count_step_expr("ged_n", c(1, 3, 10, 30, 80), GED_PAL),
2912
+ fill_opacity = 0, tooltip = grid_tooltip) |>
2913
+ # Population per grid cell -- breaks come from cached crime_mun.poblacion quantiles
2914
+ add_fill_layer(id = paste0("grid-pop-n", niv), source = src,
2915
+ fill_color = pop_qstep_expr(),
2916
+ fill_opacity = 0, tooltip = grid_tooltip) |>
2917
+ # ENVIPE on grid -- each cell carries its state's insecurity_perc (from
2918
+ # the JOIN on state_polygons by CVE_ENT in web_sql_grid_unified).
2919
+ add_fill_layer(id = paste0("grid-envipe-n", niv), source = src,
2920
+ fill_color = envipe_grid_expr("insecurity"),
2921
  fill_opacity = 0, tooltip = grid_tooltip)
2922
  m
2923
  }
 
3259
  fill_color = pam_qstep_expr(), fill_opacity = 0.7,
3260
  tooltip = list("concat", "<b>", list("get","municipio"), "</b>",
3261
  "<br>Irregular migration events: ", list("to-string", list("get","pam_n")))))
3262
+ if (!grid_on && "pop-choro" %in% active)
3263
+ lazy_add_fill("pop", "src_population", "pop-choro", function(prx, d)
3264
+ prx |> add_fill_layer("pop-choro", source = d,
3265
+ fill_color = pop_qstep_expr(), fill_opacity = 0.7,
3266
+ tooltip = list("concat", "<b>", list("get","municipio"), "</b>",
3267
+ "<br>Population: ", list("to-string", list("get","poblacion")))))
3268
  if (!grid_on && "fosas-choro" %in% active && has_fosas_tbl)
3269
  lazy_add_fill("fosas", "src_fosas", "fosas-choro", function(prx, d)
3270
  prx |> add_fill_layer("fosas-choro", source = d,
 
3276
  lazy_add_fill("state", "src_state", "envipe-state", function(prx, d)
3277
  prx |> add_fill_layer("envipe-state", source = d,
3278
  fill_color = step_expr("insecurity_perc", c(40,55,65,75,85), ENVIPE_PAL),
3279
+ fill_opacity = 0.5,
3280
  tooltip = list("concat", "<b>", list("get","desc_entidad"), "</b>",
3281
  "<br>Insecurity: ", list("to-string", list("get","insecurity_perc")), "%",
3282
  "<br>Dark figures: ", list("to-string", list("get","dark_figures_perc")), "%")))
styles.css CHANGED
@@ -2429,3 +2429,6 @@ button.mapbox-gl-draw_uncombine { display: none !important; }
2429
  .driver-popover .driver-popover-close-btn {
2430
  color: var(--c-text-dim);
2431
  }
 
 
 
 
2429
  .driver-popover .driver-popover-close-btn {
2430
  color: var(--c-text-dim);
2431
  }
2432
+
2433
+ /* Tooltip toggle: hide maplibre popups when body.tooltips-off is set */
2434
+ body.tooltips-off .maplibregl-popup { display: none !important; }