From 6afa97e0ba07be7352712458c96f522cac29dc09 Mon Sep 17 00:00:00 2001 From: dadams Date: Fri, 22 May 2026 15:12:34 -0700 Subject: [PATCH] updated map --- enhanced_data_center_cluster_map.ipynb | 91 +- ...ster_data_center_spatial_clusters_map.html | 88036 ++++++++-------- 2 files changed, 41928 insertions(+), 46199 deletions(-) diff --git a/enhanced_data_center_cluster_map.ipynb b/enhanced_data_center_cluster_map.ipynb index 829fea8..134d1b9 100644 --- a/enhanced_data_center_cluster_map.ipynb +++ b/enhanced_data_center_cluster_map.ipynb @@ -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", + " 2020
\n", + " Dem share: {fmt_number(row.election_2020_dem_share_pct, 1, suffix='%')}
\n", + " Rep share: {fmt_number(row.election_2020_rep_share_pct, 1, suffix='%')}
\n", + " Rep margin: {fmt_number(row.election_2020_rep_margin_pct, 1, suffix=' pp')}
\n", + " Dem / Rep votes: {fmt_number(row.election_2020_dem_votes)} / {fmt_number(row.election_2020_rep_votes)}
\n", + " Two-party votes: {fmt_number(row.election_2020_total_votes)}
\n", + " '''\n", " election_lines = f'''\n", "
\n", - " Election context (2020 precinct)
\n", + " Election context (precinct match)
\n", " State: {clean_value(row.election_state_code)}
\n", " Locality: {clean_value(row.election_locality)}
\n", " Precinct: {clean_value(row.election_precinct)}
\n", - " Biden share: {fmt_number(row.election_biden_share_pct, 1, suffix='%')}
\n", - " Trump share: {fmt_number(row.election_trump_share_pct, 1, suffix='%')}
\n", - " Trump margin: {fmt_number(row.election_trump_margin_pct, 1, suffix=' pp')}
\n", " Join method: {clean_value(row.election_join_method)}
\n", + " Match distance (m): {fmt_number(row.election_match_distance_m, 0)}
\n", + " {election_2020_lines}\n", " '''\n", "\n", " return folium.Popup(f'''\n", @@ -806,7 +821,7 @@ "
>= 20
\n", " \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" ] }, { diff --git a/output/enhanced_master_data_center_spatial_clusters_map.html b/output/enhanced_master_data_center_spatial_clusters_map.html index 4b5c78d..0c9cfa6 100644 --- a/output/enhanced_master_data_center_spatial_clusters_map.html +++ b/output/enhanced_master_data_center_spatial_clusters_map.html @@ -17,7 +17,7 @@