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

Upload depth_interactive_map_alpha.R

Browse files
Files changed (1) hide show
  1. depth_interactive_map_alpha.R +217 -50
depth_interactive_map_alpha.R CHANGED
@@ -183,6 +183,8 @@ GRID_LAYER_CFG <- list(
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",
@@ -202,6 +204,43 @@ FOSAS_COLS <- list(
202
  "cuerpos" = list(col = "cuerpos_total", breaks = c(1, 10, 30, 80, 200))
203
  )
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  #######################################################################################
206
  # WEB_SQL -- canonical query templates (single source for Shiny + docs converter)
207
  # Static templates use {placeholder} tokens substituted by web_sql().
@@ -335,13 +374,6 @@ WEB_SQL_TEMPLATES <- list(
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 =
@@ -576,24 +608,41 @@ web_sql_grid_unified <- function(niv, has_fosas, limit = NULL, simplify_tol = 0.
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,
588
  COALESCE(pts.ged_n, 0) AS ged_n,
589
  COALESCE(pts.infra_n, 0) AS infra_n
590
  FROM grid_nivel{niv} g
591
- LEFT JOIN crime_mun c ON g.CVEGEO = c.CVEGEO
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
  }
599
 
@@ -770,12 +819,19 @@ WEB_SPEC <- list(
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,
777
- fosas_cols = FOSAS_COLS,
778
- envipe_cols = ENVIPE_COLS,
 
 
 
779
  map_styles = MAP_STYLES,
780
  grid_layer_cfg = GRID_LAYER_CFG,
781
  non_grid_layers = NON_GRID_LAYERS,
@@ -803,9 +859,19 @@ WEB_SPEC <- list(
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"),
@@ -891,18 +957,26 @@ WEB_SPEC <- list(
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",
903
  label = "Migrant shelters and support centres",
904
  tooltip = "Shelters, food, health, government, legal and religious assistance points along migrant routes. Click a swatch in the legend to filter.",
905
- legend = list(type = "cats", pal = "infra_cats", filterable = "infra"))
 
 
 
 
 
 
 
 
 
 
906
  ))
907
  ),
908
 
@@ -921,6 +995,23 @@ WEB_SPEC <- list(
921
  fosas = list(
922
  list(label = "Clandestine graves", value = "fosas"),
923
  list(label = "Bodies (cuerpos)", value = "cuerpos")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
924
  )
925
  ),
926
 
@@ -1019,6 +1110,9 @@ LUCIDE_JS <- tags$head(
1019
  Shiny.setInputValue('crime_type', 'Homicide rate', { priority: 'event' });
1020
  Shiny.setInputValue('envipe_type', 'insecurity', { priority: 'event' });
1021
  Shiny.setInputValue('fosas_type', 'fosas', { priority: 'event' });
 
 
 
1022
  Shiny.setInputValue('grid_nivel', '6', { priority: 'event' });
1023
  }, 300);
1024
 
@@ -1239,6 +1333,30 @@ LUCIDE_JS <- tags$head(
1239
  });
1240
  }
1241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1242
  function toggleStylePicker() {
1243
  var picker = document.getElementById('style-picker');
1244
  if (!picker) return;
@@ -1879,7 +1997,9 @@ acc_radios_from_spec <- function(group) {
1879
  opts <- WEB_SPEC$radios[[group]]
1880
  cls <- paste0(group, "-radio")
1881
  setter <- switch(group, crime = "setCrimeType",
1882
- envipe = "setEnvipeType", fosas = "setFosasType")
 
 
1883
  default <- opts[[1]]$value
1884
  div(class = "acc-radios",
1885
  lapply(opts, function(op) {
@@ -2544,9 +2664,12 @@ server <- function(input, output, session) {
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
@@ -2557,16 +2680,45 @@ server <- function(input, output, session) {
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),
@@ -2585,7 +2737,10 @@ server <- function(input, output, session) {
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),
@@ -2600,7 +2755,11 @@ server <- function(input, output, session) {
2600
  }
2601
 
2602
  # Central visibility updater
2603
- apply_visibility <- function(active, grid_on, crime_type, nivel, envipe_type, fosas_type = "fosas") {
 
 
 
 
2604
  prx <- maplibre_proxy("map")
2605
 
2606
  for (orig_lid in names(GRID_LAYER_CFG)) {
@@ -2664,7 +2823,8 @@ server <- function(input, output, session) {
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
  }
@@ -2910,9 +3070,17 @@ server <- function(input, output, session) {
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).
@@ -3259,12 +3427,7 @@ server <- function(input, output, session) {
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,
@@ -3356,16 +3519,20 @@ server <- function(input, output, session) {
3356
  observeEvent(
3357
  eventExpr = list(
3358
  input$active_layers, is_grid_on(),
3359
- input$crime_type, input$grid_nivel, input$envipe_type, input$fosas_type
 
3360
  ),
3361
  handlerExpr = {
3362
  apply_visibility(
3363
- active = input$active_layers %||% character(0),
3364
- grid_on = is_grid_on(),
3365
- crime_type = input$crime_type %||% "Homicide rate",
3366
- nivel = input$grid_nivel %||% "6",
3367
- envipe_type = input$envipe_type %||% "insecurity",
3368
- fosas_type = input$fosas_type %||% "fosas"
 
 
 
3369
  )
3370
  },
3371
  ignoreInit = TRUE
 
183
  "pam-choro" = "pam",
184
  "envipe-state" = "envipe", # ENVIPE now also paints per grid cell
185
  "pop-choro" = "pop",
186
+ "patp-choro" = "patp",
187
+ "pobreza-choro" = "pobreza",
188
  "migrants-pts" = "migrants",
189
  "graves-pts" = "graves",
190
  "infra-pts" = "infra",
 
204
  "cuerpos" = list(col = "cuerpos_total", breaks = c(1, 10, 30, 80, 200))
205
  )
206
 
207
+ # CPV 2020 population sub-types -- per grid cell (codigo + nivel).
208
+ # Breaks chosen on observed niv=4..8 ranges; map at any nivel is readable.
209
+ POP_COLS <- list(
210
+ "POBTOT" = list(col = "pobtot", label = "Total population",
211
+ breaks = c(100, 1000, 5000, 25000, 100000, 500000)),
212
+ "PNACOP" = list(col = "pnacop", label = "Born abroad",
213
+ breaks = c(5, 25, 100, 500, 2500, 10000)),
214
+ "PRESOP2015" = list(col = "presop2015", label = "Resident abroad (2015)",
215
+ breaks = c(2, 10, 50, 250, 1000, 5000))
216
+ )
217
+
218
+ # PATP sub-types (kind controls paint family: "num" = step, "cat" = match).
219
+ PATP_COLS <- list(
220
+ "t_part" = list(col = "t_part", kind = "num", label = "Participation rate",
221
+ breaks = c(35, 45, 50, 55, 60, 70)),
222
+ "t_ocup" = list(col = "t_ocup", kind = "num", label = "Occupation rate",
223
+ breaks = c(92, 95, 97, 98, 99, 99.5)),
224
+ "pr" = list(col = "pr", kind = "num", label = "PR indicator",
225
+ breaks = c(0.5, 2, 5, 10, 25, 50)),
226
+ "ind_conec" = list(col = "ind_conec", kind = "num", label = "Connectivity index",
227
+ breaks = c(0.15, 0.25, 0.35, 0.45, 0.6, 0.75)),
228
+ "pr_pred_loc_rur" = list(col = "pr_pred_loc_rur", kind = "cat",
229
+ label = "Predominant rural problem",
230
+ palette = "patp_rural"),
231
+ "delitos_fc_b_j_a" = list(col = "delitos_fc_b_j_a", kind = "cat",
232
+ label = "Common-jurisdiction crime focus",
233
+ palette = "patp_delito")
234
+ )
235
+
236
+ # Pobreza (CONEVAL) -- only the 2 the user asked for in this round.
237
+ POBREZA_COLS <- list(
238
+ "pobreza_pct" = list(col = "pobreza_pct", label = "Poverty (%)",
239
+ breaks = c(20, 35, 50, 65, 80, 90)),
240
+ "pobreza_ext_pct" = list(col = "pobreza_ext_pct", label = "Extreme poverty (%)",
241
+ breaks = c(2, 5, 10, 20, 35, 55))
242
+ )
243
+
244
  #######################################################################################
245
  # WEB_SQL -- canonical query templates (single source for Shiny + docs converter)
246
  # Static templates use {placeholder} tokens substituted by web_sql().
 
374
  FROM dim_pam_mun GROUP BY CVEGEO) p
375
  ON c.CVEGEO = p.CVEGEO",
376
 
 
 
 
 
 
 
 
377
  # patp: dim_patp_mun stores numeric values as VARCHAR; TRY_CAST converts
378
  # the 'n.d.' sentinels (and other non-numeric strings) to NULL.
379
  src_patp =
 
608
  CAST(c.secuestro AS DOUBLE) AS secuestro,
609
  CAST(c.trafico_menores AS DOUBLE) AS trafico_menores,
610
  CAST(c.trata_personas AS DOUBLE) AS trata_personas,
611
+ -- CPV 2020 per-cell population (joined by codigo + nivel); -6 is
612
+ -- INEGI's confidentiality suppression marker, normalised to NULL.
613
+ CASE WHEN cpv.pobtot < 0 THEN NULL ELSE CAST(cpv.pobtot AS DOUBLE) END AS pobtot,
614
+ CASE WHEN cpv.pnacop < 0 THEN NULL ELSE CAST(cpv.pnacop AS DOUBLE) END AS pnacop,
615
+ CASE WHEN cpv.presop2015 < 0 THEN NULL ELSE CAST(cpv.presop2015 AS DOUBLE) END AS presop2015,
616
  COALESCE(CAST(d.desap_rate AS DOUBLE), 0) AS desap_per_100k{fosas_cols},
617
  COALESCE(r.giim_mun, 'No aplica') AS giim_mun,
618
  COALESCE(CAST(p.pam_total AS INTEGER), 0) AS pam_n,
619
  CAST(s.perception_insecurity_perc AS DOUBLE) AS insecurity_perc,
620
  CAST(s.dark_figures_perc AS DOUBLE) AS dark_figures_perc,
621
+ -- patp_mun (numeric-as-VARCHAR -> TRY_CAST; categorical kept as string)
622
+ TRY_CAST(patp.t_part AS DOUBLE) AS t_part,
623
+ TRY_CAST(patp.t_ocup AS DOUBLE) AS t_ocup,
624
+ TRY_CAST(patp.pr AS DOUBLE) AS pr,
625
+ CAST(patp.ind_conec AS DOUBLE) AS ind_conec,
626
+ patp.pr_pred_loc_rur AS pr_pred_loc_rur,
627
+ patp.delitos_fc_b_j_a AS delitos_fc_b_j_a,
628
+ -- pobreza_mun (CONEVAL 2020)
629
+ CAST(pob.pobreza_pct AS DOUBLE) AS pobreza_pct,
630
+ CAST(pob.pobreza_ext_pct AS DOUBLE) AS pobreza_ext_pct,
631
  COALESCE(pts.migrants_n, 0) AS migrants_n,
632
  COALESCE(pts.graves_n, 0) AS graves_n,
633
  COALESCE(pts.ocved_n, 0) AS ocved_n,
634
  COALESCE(pts.ged_n, 0) AS ged_n,
635
  COALESCE(pts.infra_n, 0) AS infra_n
636
  FROM grid_nivel{niv} g
637
+ LEFT JOIN crime_mun c ON g.CVEGEO = c.CVEGEO
638
+ LEFT JOIN {desap_sub} d ON g.CVEGEO = d.CVEGEO{fosas_join}
639
+ LEFT JOIN dim_remesas_mun r ON g.CVEGEO = r.CVEGEO
640
+ LEFT JOIN {pam_sub} p ON g.CVEGEO = p.CVEGEO
641
+ LEFT JOIN state_polygons s ON s.CVE_ENT = SUBSTR(g.CVEGEO, 1, 2)
642
+ LEFT JOIN dim_pop_cpv_grid cpv ON cpv.codigo = g.codigo AND cpv.nivel = {niv}
643
+ LEFT JOIN dim_patp_mun patp ON patp.CVEGEO = g.CVEGEO
644
+ LEFT JOIN dim_pobreza_mun pob ON pob.CVEGEO = g.CVEGEO
645
+ LEFT JOIN pts_src pts ON g.codigo = pts.codigo{limit_clause}"
646
  )
647
  }
648
 
 
819
  ocved = OCVED_PAL, ged = GED_PAL,
820
  giim = GIIM_COLORS, bar_ramp = BAR_RAMP,
821
  infra_cats = INFRA_CATS,
822
+ pop = POP_PAL,
823
+ patp = PATP_PAL,
824
+ pobreza = POBREZA_PAL,
825
+ patp_rural = PATP_RURAL_PROBLEM_PAL,
826
+ patp_delito = PATP_DELITO_PAL
827
  ),
828
 
829
+ crime_cols = CRIME_COLS,
830
+ fosas_cols = FOSAS_COLS,
831
+ envipe_cols = ENVIPE_COLS,
832
+ pop_cols = POP_COLS,
833
+ patp_cols = PATP_COLS,
834
+ pobreza_cols = POBREZA_COLS,
835
  map_styles = MAP_STYLES,
836
  grid_layer_cfg = GRID_LAYER_CFG,
837
  non_grid_layers = NON_GRID_LAYERS,
 
859
  list(id = "pam-choro", type = "fill", src = "src_pam",
860
  geom = "polygon", color = "pam_qstep", value = "pam_n",
861
  opacity = 0.7, visible = FALSE),
862
+ # pop-choro is GRID-ONLY: data lives in dim_pop_cpv_grid keyed by (codigo,
863
+ # nivel). The fill is painted directly on grid cells via add_typed_grid_layers,
864
+ # there is no mun-level fallback layer.
865
+ list(id = "pop-choro", type = "fill", src = NULL,
866
+ geom = "grid_only", color = "pop_qstep", value = "pobtot",
867
  opacity = 0.7, visible = FALSE),
868
+ # patp-choro and pobreza-choro are also GRID-ONLY (joined into grid_unified)
869
+ list(id = "patp-choro", type = "fill", src = NULL,
870
+ geom = "grid_only", color = "patp_qstep", value = "t_part",
871
+ opacity = 0.7, visible = FALSE, requires = "dim_patp_mun"),
872
+ list(id = "pobreza-choro", type = "fill", src = NULL,
873
+ geom = "grid_only", color = "pobreza_qstep", value = "pobreza_pct",
874
+ opacity = 0.7, visible = FALSE, requires = "dim_pobreza_mun"),
875
  list(id = "fosas-choro", type = "fill", src = "src_fosas",
876
  geom = "polygon", color = "fosas_step", value = "fosas_total",
877
  opacity = 0.65, visible = FALSE, requires = "dim_fosas_mun"),
 
957
  tooltip = "Railway lines used by migrants, including freight routes known as 'La Bestia'.",
958
  legend = list(type = "line", color = "#9E2A3A", label = "Railway", dashed = TRUE)),
959
  list(kind = "layer", value = "pop-choro",
960
+ label = "Population per grid cell (CPV 2020)",
961
+ tooltip = "Census 2020 population counts joined to each rHEALPix grid cell at the active nivel. Enable the grid view to see this layer. Select sub-type: total population, born abroad, or resident abroad in 2015.",
962
  legend = list(type = "ramp", pal = "pop", unit = " people")),
963
+ list(kind = "radios", group = "pop")
 
 
964
  )),
965
  list(id = "vulnerability", label = "Vulnerability", open = FALSE, items = list(
966
  list(kind = "layer", value = "infra-pts",
967
  label = "Migrant shelters and support centres",
968
  tooltip = "Shelters, food, health, government, legal and religious assistance points along migrant routes. Click a swatch in the legend to filter.",
969
+ legend = list(type = "cats", pal = "infra_cats", filterable = "infra")),
970
+ list(kind = "layer", value = "patp-choro",
971
+ label = "PATP-CONEVAL municipal indicators",
972
+ tooltip = "Multi-dimensional CONEVAL PATP indicators by municipality. Numeric sub-types render as a percentile choropleth; categorical sub-types (rural problem / crime focus) render with a fixed palette. Enable the grid view to see this layer.",
973
+ legend = list(type = "ramp", pal = "patp", unit = "")),
974
+ list(kind = "radios", group = "patp"),
975
+ list(kind = "layer", value = "pobreza-choro",
976
+ label = "Poverty (CONEVAL 2020)",
977
+ tooltip = "CONEVAL 2020 poverty / extreme poverty share by municipality. Enable the grid view to see this layer.",
978
+ legend = list(type = "ramp", pal = "pobreza", unit = " %")),
979
+ list(kind = "radios", group = "pobreza")
980
  ))
981
  ),
982
 
 
995
  fosas = list(
996
  list(label = "Clandestine graves", value = "fosas"),
997
  list(label = "Bodies (cuerpos)", value = "cuerpos")
998
+ ),
999
+ pop = list(
1000
+ list(label = "Total population", value = "POBTOT"),
1001
+ list(label = "Born abroad", value = "PNACOP"),
1002
+ list(label = "Resident abroad (2015)", value = "PRESOP2015")
1003
+ ),
1004
+ patp = list(
1005
+ list(label = "Participation rate", value = "t_part"),
1006
+ list(label = "Occupation rate", value = "t_ocup"),
1007
+ list(label = "PR indicator", value = "pr"),
1008
+ list(label = "Connectivity index", value = "ind_conec"),
1009
+ list(label = "Predominant rural problem", value = "pr_pred_loc_rur"),
1010
+ list(label = "Common-jurisdiction crime", value = "delitos_fc_b_j_a")
1011
+ ),
1012
+ pobreza = list(
1013
+ list(label = "Poverty (%)", value = "pobreza_pct"),
1014
+ list(label = "Extreme poverty (%)", value = "pobreza_ext_pct")
1015
  )
1016
  ),
1017
 
 
1110
  Shiny.setInputValue('crime_type', 'Homicide rate', { priority: 'event' });
1111
  Shiny.setInputValue('envipe_type', 'insecurity', { priority: 'event' });
1112
  Shiny.setInputValue('fosas_type', 'fosas', { priority: 'event' });
1113
+ Shiny.setInputValue('pop_type', 'POBTOT', { priority: 'event' });
1114
+ Shiny.setInputValue('patp_type', 't_part', { priority: 'event' });
1115
+ Shiny.setInputValue('pobreza_type','pobreza_pct', { priority: 'event' });
1116
  Shiny.setInputValue('grid_nivel', '6', { priority: 'event' });
1117
  }, 300);
1118
 
 
1333
  });
1334
  }
1335
 
1336
+ function setPopType(type) {
1337
+ if (typeof Shiny !== 'undefined')
1338
+ Shiny.setInputValue('pop_type', type, { priority: 'event' });
1339
+ document.querySelectorAll('.pop-radio').forEach(function(r) {
1340
+ r.classList.toggle('active', r.dataset.value === type);
1341
+ });
1342
+ }
1343
+
1344
+ function setPATPType(type) {
1345
+ if (typeof Shiny !== 'undefined')
1346
+ Shiny.setInputValue('patp_type', type, { priority: 'event' });
1347
+ document.querySelectorAll('.patp-radio').forEach(function(r) {
1348
+ r.classList.toggle('active', r.dataset.value === type);
1349
+ });
1350
+ }
1351
+
1352
+ function setPobrezaType(type) {
1353
+ if (typeof Shiny !== 'undefined')
1354
+ Shiny.setInputValue('pobreza_type', type, { priority: 'event' });
1355
+ document.querySelectorAll('.pobreza-radio').forEach(function(r) {
1356
+ r.classList.toggle('active', r.dataset.value === type);
1357
+ });
1358
+ }
1359
+
1360
  function toggleStylePicker() {
1361
  var picker = document.getElementById('style-picker');
1362
  if (!picker) return;
 
1997
  opts <- WEB_SPEC$radios[[group]]
1998
  cls <- paste0(group, "-radio")
1999
  setter <- switch(group, crime = "setCrimeType",
2000
+ envipe = "setEnvipeType", fosas = "setFosasType",
2001
+ pop = "setPopType", patp = "setPATPType",
2002
+ pobreza = "setPobrezaType")
2003
  default <- opts[[1]]$value
2004
  div(class = "acc-radios",
2005
  lapply(opts, function(op) {
 
2664
  count_step_expr("pam_n", breaks, PAM_PAL)
2665
  }
2666
 
2667
+ # CPV 2020 per-cell population. The sub-type (POBTOT / PNACOP / PRESOP2015)
2668
+ # determines which column + break set drives the step paint. NULL cells
2669
+ # (INEGI confidentiality suppression) fall to the lightest palette stop.
2670
+ pop_grid_expr <- function(pop_type = "POBTOT") {
2671
+ info <- POP_COLS[[pop_type]] %||% POP_COLS[["POBTOT"]]
2672
+ step_expr(info$col, info$breaks, POP_PAL)
2673
  }
2674
 
2675
  # Envipe on the GRID: each grid cell carries its containing state's
 
2680
  step_expr(info$col, info$breaks, ENVIPE_PAL)
2681
  }
2682
 
2683
+ # PATP-CONEVAL on the GRID. Each sub-type is either "num" (step paint via
2684
+ # TRY_CAST'd column) or "cat" (match paint over a fixed Spanish-label palette).
2685
+ patp_grid_expr <- function(patp_type = "t_part") {
2686
+ info <- PATP_COLS[[patp_type]] %||% PATP_COLS[["t_part"]]
2687
+ if (identical(info$kind, "cat")) {
2688
+ pal <- if (identical(info$palette, "patp_rural")) PATP_RURAL_PROBLEM_PAL
2689
+ else if (identical(info$palette, "patp_delito")) PATP_DELITO_PAL
2690
+ else PATP_RURAL_PROBLEM_PAL
2691
+ pairs <- unlist(
2692
+ lapply(names(pal), function(nm) list(nm, unname(pal[[nm]]))),
2693
+ recursive = FALSE
2694
+ )
2695
+ c(list("match", list("get", info$col)), pairs, list("#bdbdbd"))
2696
+ } else {
2697
+ step_expr(info$col, info$breaks, PATP_PAL)
2698
+ }
2699
+ }
2700
+
2701
+ # Pobreza (CONEVAL 2020). Two numeric sub-types only.
2702
+ pobreza_grid_expr <- function(pobreza_type = "pobreza_pct") {
2703
+ info <- POBREZA_COLS[[pobreza_type]] %||% POBREZA_COLS[["pobreza_pct"]]
2704
+ step_expr(info$col, info$breaks, POBREZA_PAL)
2705
+ }
2706
+
2707
  # Return the fill-color expression for a given original layer ID
2708
  grid_color_expr <- function(orig_lid, crime_type, fosas_type = "fosas",
2709
+ envipe_type = "insecurity",
2710
+ pop_type = "POBTOT",
2711
+ patp_type = "t_part",
2712
+ pobreza_type = "pobreza_pct") {
2713
  switch(orig_lid,
2714
  "crime-choro" = crime_qstep_expr(crime_type),
2715
  "desap-choro" = desap_qstep_expr(),
2716
  "fosas-choro" = fosas_step_expr(fosas_type),
2717
  "remesas-choro" = giim_match_expr(),
2718
  "pam-choro" = pam_qstep_expr(),
2719
+ "pop-choro" = pop_grid_expr(pop_type),
2720
+ "patp-choro" = patp_grid_expr(patp_type),
2721
+ "pobreza-choro" = pobreza_grid_expr(pobreza_type),
2722
  "envipe-state" = envipe_grid_expr(envipe_type),
2723
  "migrants-pts" = count_step_expr("migrants_n", c(1, 5, 20, 50, 100), MIGRANTS_PAL),
2724
  "graves-pts" = count_step_expr("graves_n", c(1, 2, 5, 15, 30), GRAVES_PAL),
 
2737
  "desap-choro" = isTRUE(lazy_added$desap),
2738
  "remesas-choro" = isTRUE(lazy_added$remesas),
2739
  "pam-choro" = isTRUE(lazy_added$pam),
2740
+ # pop / patp / pobreza are grid-only -- no mun-level layer to gate.
2741
+ "pop-choro" = FALSE,
2742
+ "patp-choro" = FALSE,
2743
+ "pobreza-choro" = FALSE,
2744
  "fosas-choro" = isTRUE(lazy_added$fosas),
2745
  "envipe-state" = isTRUE(lazy_added$state),
2746
  "routes-lines" = isTRUE(lazy_added$routes),
 
2755
  }
2756
 
2757
  # Central visibility updater
2758
+ apply_visibility <- function(active, grid_on, crime_type, nivel, envipe_type,
2759
+ fosas_type = "fosas",
2760
+ pop_type = "POBTOT",
2761
+ patp_type = "t_part",
2762
+ pobreza_type = "pobreza_pct") {
2763
  prx <- maplibre_proxy("map")
2764
 
2765
  for (orig_lid in names(GRID_LAYER_CFG)) {
 
2823
  show <- grid_on && orig_lid %in% active && niv == nivel
2824
  prx <- prx |> set_paint_property(grid_id, "fill-opacity", if (show) 0.65 else 0)
2825
  if (show) {
2826
+ expr <- grid_color_expr(orig_lid, crime_type, fosas_type, envipe_type,
2827
+ pop_type, patp_type, pobreza_type)
2828
  if (!is.null(expr))
2829
  prx <- prx |> set_paint_property(grid_id, "fill-color", expr)
2830
  }
 
3070
  add_fill_layer(id = paste0("grid-ged-n", niv), source = src,
3071
  fill_color = count_step_expr("ged_n", c(1, 3, 10, 30, 80), GED_PAL),
3072
  fill_opacity = 0, tooltip = grid_tooltip) |>
3073
+ # CPV 2020 population per grid cell -- default sub-type POBTOT
3074
  add_fill_layer(id = paste0("grid-pop-n", niv), source = src,
3075
+ fill_color = pop_grid_expr("POBTOT"),
3076
+ fill_opacity = 0, tooltip = grid_tooltip) |>
3077
+ # PATP indicators (numeric default = t_part)
3078
+ add_fill_layer(id = paste0("grid-patp-n", niv), source = src,
3079
+ fill_color = patp_grid_expr("t_part"),
3080
+ fill_opacity = 0, tooltip = grid_tooltip) |>
3081
+ # CONEVAL pobreza (default = poverty %)
3082
+ add_fill_layer(id = paste0("grid-pobreza-n", niv), source = src,
3083
+ fill_color = pobreza_grid_expr("pobreza_pct"),
3084
  fill_opacity = 0, tooltip = grid_tooltip) |>
3085
  # ENVIPE on grid -- each cell carries its state's insecurity_perc (from
3086
  # the JOIN on state_polygons by CVE_ENT in web_sql_grid_unified).
 
3427
  fill_color = pam_qstep_expr(), fill_opacity = 0.7,
3428
  tooltip = list("concat", "<b>", list("get","municipio"), "</b>",
3429
  "<br>Irregular migration events: ", list("to-string", list("get","pam_n")))))
3430
+ # pop-choro is GRID-ONLY (CPV 2020 per cell). No mun-level fallback.
 
 
 
 
 
3431
  if (!grid_on && "fosas-choro" %in% active && has_fosas_tbl)
3432
  lazy_add_fill("fosas", "src_fosas", "fosas-choro", function(prx, d)
3433
  prx |> add_fill_layer("fosas-choro", source = d,
 
3519
  observeEvent(
3520
  eventExpr = list(
3521
  input$active_layers, is_grid_on(),
3522
+ input$crime_type, input$grid_nivel, input$envipe_type, input$fosas_type,
3523
+ input$pop_type, input$patp_type, input$pobreza_type
3524
  ),
3525
  handlerExpr = {
3526
  apply_visibility(
3527
+ active = input$active_layers %||% character(0),
3528
+ grid_on = is_grid_on(),
3529
+ crime_type = input$crime_type %||% "Homicide rate",
3530
+ nivel = input$grid_nivel %||% "6",
3531
+ envipe_type = input$envipe_type %||% "insecurity",
3532
+ fosas_type = input$fosas_type %||% "fosas",
3533
+ pop_type = input$pop_type %||% "POBTOT",
3534
+ patp_type = input$patp_type %||% "t_part",
3535
+ pobreza_type = input$pobreza_type %||% "pobreza_pct"
3536
  )
3537
  },
3538
  ignoreInit = TRUE