updated map

This commit is contained in:
2026-05-22 15:12:34 -07:00
parent 03239ad007
commit 6afa97e0ba
2 changed files with 41928 additions and 46199 deletions

View File

@@ -94,6 +94,8 @@
"SHOW_CLIMATE_LAYER = True\n",
"SHOW_BROADBAND_LAYER = True\n",
"SHOW_ELECTION_LAYER = True\n",
"SHOW_ELECTION_2020_LAYER = True\n",
"SHOW_ELECTION_2024_LAYER = False\n",
"\n",
"OUTPUT_DIR.mkdir(exist_ok=True)\n",
"print('points:', POINTS_CSV)\n",
@@ -356,39 +358,42 @@
" )\n",
" select\n",
" master_id, election_state_code, election_join_method, election_match_distance_m,\n",
" feature_id, layer_id, election_latitude, election_longitude,\n",
" feature_id, layer_id as election_layer_id, election_latitude, election_longitude, properties,\n",
" coalesce((properties->>'LOCALITY'), '') as election_locality,\n",
" coalesce((properties->>'PRECINCT'), '') as election_precinct,\n",
" nullif(properties->>'G20PREDBID','')::double precision as election_biden_votes,\n",
" nullif(properties->>'G20PRERTRU','')::double precision as election_trump_votes,\n",
" case\n",
" when (coalesce(nullif(properties->>'G20PREDBID','')::double precision,0)\n",
" + coalesce(nullif(properties->>'G20PRERTRU','')::double precision,0)) > 0\n",
" then 100.0 * coalesce(nullif(properties->>'G20PREDBID','')::double precision,0)\n",
" / (coalesce(nullif(properties->>'G20PREDBID','')::double precision,0)\n",
" + coalesce(nullif(properties->>'G20PRERTRU','')::double precision,0))\n",
" end as election_biden_share_pct,\n",
" case\n",
" when (coalesce(nullif(properties->>'G20PREDBID','')::double precision,0)\n",
" + coalesce(nullif(properties->>'G20PRERTRU','')::double precision,0)) > 0\n",
" then 100.0 * coalesce(nullif(properties->>'G20PRERTRU','')::double precision,0)\n",
" / (coalesce(nullif(properties->>'G20PREDBID','')::double precision,0)\n",
" + coalesce(nullif(properties->>'G20PRERTRU','')::double precision,0))\n",
" end as election_trump_share_pct\n",
" nullif(properties->>'G20PREDBID','')::double precision as election_2020_dem_votes,\n",
" nullif(properties->>'G20PRERTRU','')::double precision as election_2020_rep_votes\n",
" from best_match\n",
" \"\"\"\n",
" election_context = pd.read_sql(election_sql, conn)\n",
" if not election_context.empty:\n",
" election_context['election_trump_margin_pct'] = (\n",
" election_context['election_trump_share_pct'] - election_context['election_biden_share_pct']\n",
" election_context['election_2020_total_votes'] = (\n",
" election_context['election_2020_dem_votes'].fillna(0) + election_context['election_2020_rep_votes'].fillna(0)\n",
" )\n",
" election_context.loc[election_context['election_2020_total_votes'].eq(0), 'election_2020_total_votes'] = pd.NA\n",
"\n",
"\n",
" election_context['election_2020_dem_share_pct'] = 100.0 * election_context['election_2020_dem_votes'] / election_context['election_2020_total_votes']\n",
" election_context['election_2020_rep_share_pct'] = 100.0 * election_context['election_2020_rep_votes'] / election_context['election_2020_total_votes']\n",
" election_context['election_2020_rep_margin_pct'] = (\n",
" election_context['election_2020_rep_share_pct'] - election_context['election_2020_dem_share_pct']\n",
" )\n",
"\n",
"\n",
" election_context['election_biden_votes'] = election_context['election_2020_dem_votes']\n",
" election_context['election_trump_votes'] = election_context['election_2020_rep_votes']\n",
" election_context['election_biden_share_pct'] = election_context['election_2020_dem_share_pct']\n",
" election_context['election_trump_share_pct'] = election_context['election_2020_rep_share_pct']\n",
" election_context['election_trump_margin_pct'] = election_context['election_2020_rep_margin_pct']\n",
" election_context = election_context.drop(columns=['properties'])\n",
"\n",
" print(f'election_context rows: {len(election_context):,}')\n",
" if not election_context.empty:\n",
" cols = [c for c in election_context.columns if c != 'master_id']\n",
" points = points.merge(election_context[['master_id'] + cols], on='master_id', how='left')\n",
"\n",
"\n",
"load_optional_db_layers()"
"load_optional_db_layers()\n"
]
},
{
@@ -615,17 +620,27 @@
" '''\n",
"\n",
" election_lines = ''\n",
" if hasattr(row, 'election_biden_share_pct') and pd.notna(row.election_biden_share_pct):\n",
" has_2020 = hasattr(row, 'election_2020_dem_share_pct') and pd.notna(row.election_2020_dem_share_pct)\n",
" if has_2020:\n",
" election_2020_lines = ''\n",
" if has_2020:\n",
" election_2020_lines = f'''\n",
" <strong>2020</strong><br>\n",
" Dem share: {fmt_number(row.election_2020_dem_share_pct, 1, suffix='%')}<br>\n",
" Rep share: {fmt_number(row.election_2020_rep_share_pct, 1, suffix='%')}<br>\n",
" Rep margin: {fmt_number(row.election_2020_rep_margin_pct, 1, suffix=' pp')}<br>\n",
" Dem / Rep votes: {fmt_number(row.election_2020_dem_votes)} / {fmt_number(row.election_2020_rep_votes)}<br>\n",
" Two-party votes: {fmt_number(row.election_2020_total_votes)}<br>\n",
" '''\n",
" election_lines = f'''\n",
" <hr style=\"margin: 6px 0;\">\n",
" <strong>Election context (2020 precinct)</strong><br>\n",
" <strong>Election context (precinct match)</strong><br>\n",
" State: {clean_value(row.election_state_code)}<br>\n",
" Locality: {clean_value(row.election_locality)}<br>\n",
" Precinct: {clean_value(row.election_precinct)}<br>\n",
" Biden share: {fmt_number(row.election_biden_share_pct, 1, suffix='%')}<br>\n",
" Trump share: {fmt_number(row.election_trump_share_pct, 1, suffix='%')}<br>\n",
" Trump margin: {fmt_number(row.election_trump_margin_pct, 1, suffix=' pp')}<br>\n",
" Join method: {clean_value(row.election_join_method)}<br>\n",
" Match distance (m): {fmt_number(row.election_match_distance_m, 0)}<br>\n",
" {election_2020_lines}\n",
" '''\n",
"\n",
" return folium.Popup(f'''\n",
@@ -806,7 +821,7 @@
" <div><span style=\"display:inline-block;width:10px;height:10px;background:#7f1d1d;margin-right:6px;\"></span>&gt;= 20</div>\n",
" </div>\n",
" \"\"\"\n",
" map_obj.get_root().html.add_child(folium.Element(legend_html))"
" map_obj.get_root().html.add_child(folium.Element(legend_html))\n"
]
},
{
@@ -836,7 +851,7 @@
" opposition_layer = folium.FeatureGroup(name='Opposition cases', show=False)\n",
" climate_layer = folium.FeatureGroup(name='Climate stress context', show=False)\n",
" broadband_layer = folium.FeatureGroup(name='Broadband capacity context', show=False)\n",
" election_layer = folium.FeatureGroup(name='Election context (2020 precinct match)', show=False)\n",
" election_2020_layer = folium.FeatureGroup(name='Election context (2020 precinct match)', show=False)\n",
" clustered_layer = folium.FeatureGroup(name='Data centers: clustered', show=True)\n",
" noise_layer = folium.FeatureGroup(name='Data centers: noise / isolated', show=True)\n",
" centroid_layer = folium.FeatureGroup(name='Cluster centroids and p90 radius', show=True)\n",
@@ -935,15 +950,16 @@
" tooltip=f\"Broadband: providers {fmt_number(row.fcc_provider_count)}; max down {fmt_number(row.fcc_max_advertised_download_mbps, 0, suffix=' Mbps')}\",\n",
" ).add_to(broadband_layer)\n",
"\n",
" if SHOW_ELECTION_LAYER and not election_context.empty:\n",
" for row in election_context.dropna(subset=['election_latitude', 'election_longitude']).itertuples(index=False):\n",
" margin = getattr(row, 'election_trump_margin_pct')\n",
" if SHOW_ELECTION_LAYER and SHOW_ELECTION_2020_LAYER and not election_context.empty:\n",
" e20_rows = election_context.dropna(subset=['election_latitude', 'election_longitude', 'election_2020_rep_margin_pct'])\n",
" for row in e20_rows.itertuples(index=False):\n",
" margin = getattr(row, 'election_2020_rep_margin_pct')\n",
" color = election_color(margin)\n",
" radius = max(4, min(11, 4 + abs(float(margin)) / 8.0)) if pd.notna(margin) else 5\n",
" tip = (\n",
" f\"Election precinct: {row.election_state_code} {clean_value(row.election_locality)}; \"\n",
" f\"Biden {fmt_number(row.election_biden_share_pct, 1, suffix='%')} / \"\n",
" f\"Trump {fmt_number(row.election_trump_share_pct, 1, suffix='%')}\"\n",
" f\"Election 2020: {row.election_state_code} {clean_value(row.election_locality)}; \"\n",
" f\"Dem {fmt_number(row.election_2020_dem_share_pct, 1, suffix='%')} / \"\n",
" f\"Rep {fmt_number(row.election_2020_rep_share_pct, 1, suffix='%')}\"\n",
" )\n",
" folium.CircleMarker(\n",
" location=[row.election_latitude, row.election_longitude],\n",
@@ -951,10 +967,11 @@
" color=color,\n",
" fill=True,\n",
" fill_color=color,\n",
" fill_opacity=0.4,\n",
" fill_opacity=0.35,\n",
" weight=1,\n",
" tooltip=tip,\n",
" ).add_to(election_layer)\n",
" ).add_to(election_2020_layer)\n",
"\n",
"\n",
" bounds = []\n",
" for row in points_df.itertuples(index=False):\n",
@@ -1005,7 +1022,7 @@
" opposition_layer.add_to(m)\n",
" climate_layer.add_to(m)\n",
" broadband_layer.add_to(m)\n",
" election_layer.add_to(m)\n",
" election_2020_layer.add_to(m)\n",
" clustered_layer.add_to(m)\n",
" noise_layer.add_to(m)\n",
" centroid_layer.add_to(m)\n",
@@ -1035,7 +1052,7 @@
"outputs": [],
"source": [
"cluster_map.save(MAP_HTML)\n",
"print('Wrote:', MAP_HTML.resolve())"
"print('Wrote:', MAP_HTML.resolve())\n"
]
},
{