updated map
This commit is contained in:
@@ -94,6 +94,8 @@
|
|||||||
"SHOW_CLIMATE_LAYER = True\n",
|
"SHOW_CLIMATE_LAYER = True\n",
|
||||||
"SHOW_BROADBAND_LAYER = True\n",
|
"SHOW_BROADBAND_LAYER = True\n",
|
||||||
"SHOW_ELECTION_LAYER = True\n",
|
"SHOW_ELECTION_LAYER = True\n",
|
||||||
|
"SHOW_ELECTION_2020_LAYER = True\n",
|
||||||
|
"SHOW_ELECTION_2024_LAYER = False\n",
|
||||||
"\n",
|
"\n",
|
||||||
"OUTPUT_DIR.mkdir(exist_ok=True)\n",
|
"OUTPUT_DIR.mkdir(exist_ok=True)\n",
|
||||||
"print('points:', POINTS_CSV)\n",
|
"print('points:', POINTS_CSV)\n",
|
||||||
@@ -356,39 +358,42 @@
|
|||||||
" )\n",
|
" )\n",
|
||||||
" select\n",
|
" select\n",
|
||||||
" master_id, election_state_code, election_join_method, election_match_distance_m,\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->>'LOCALITY'), '') as election_locality,\n",
|
||||||
" coalesce((properties->>'PRECINCT'), '') as election_precinct,\n",
|
" coalesce((properties->>'PRECINCT'), '') as election_precinct,\n",
|
||||||
" nullif(properties->>'G20PREDBID','')::double precision as election_biden_votes,\n",
|
" nullif(properties->>'G20PREDBID','')::double precision as election_2020_dem_votes,\n",
|
||||||
" nullif(properties->>'G20PRERTRU','')::double precision as election_trump_votes,\n",
|
" nullif(properties->>'G20PRERTRU','')::double precision as election_2020_rep_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",
|
|
||||||
" from best_match\n",
|
" from best_match\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" election_context = pd.read_sql(election_sql, conn)\n",
|
" election_context = pd.read_sql(election_sql, conn)\n",
|
||||||
" if not election_context.empty:\n",
|
" if not election_context.empty:\n",
|
||||||
" election_context['election_trump_margin_pct'] = (\n",
|
" election_context['election_2020_total_votes'] = (\n",
|
||||||
" election_context['election_trump_share_pct'] - election_context['election_biden_share_pct']\n",
|
" election_context['election_2020_dem_votes'].fillna(0) + election_context['election_2020_rep_votes'].fillna(0)\n",
|
||||||
" )\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",
|
" print(f'election_context rows: {len(election_context):,}')\n",
|
||||||
" if not election_context.empty:\n",
|
" if not election_context.empty:\n",
|
||||||
" cols = [c for c in election_context.columns if c != 'master_id']\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",
|
" points = points.merge(election_context[['master_id'] + cols], on='master_id', how='left')\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"load_optional_db_layers()"
|
"load_optional_db_layers()\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -615,17 +620,27 @@
|
|||||||
" '''\n",
|
" '''\n",
|
||||||
"\n",
|
"\n",
|
||||||
" election_lines = ''\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",
|
" election_lines = f'''\n",
|
||||||
" <hr style=\"margin: 6px 0;\">\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",
|
" State: {clean_value(row.election_state_code)}<br>\n",
|
||||||
" Locality: {clean_value(row.election_locality)}<br>\n",
|
" Locality: {clean_value(row.election_locality)}<br>\n",
|
||||||
" Precinct: {clean_value(row.election_precinct)}<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",
|
" 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",
|
||||||
"\n",
|
"\n",
|
||||||
" return folium.Popup(f'''\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>>= 20</div>\n",
|
" <div><span style=\"display:inline-block;width:10px;height:10px;background:#7f1d1d;margin-right:6px;\"></span>>= 20</div>\n",
|
||||||
" </div>\n",
|
" </div>\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",
|
" opposition_layer = folium.FeatureGroup(name='Opposition cases', show=False)\n",
|
||||||
" climate_layer = folium.FeatureGroup(name='Climate stress context', 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",
|
" 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",
|
" clustered_layer = folium.FeatureGroup(name='Data centers: clustered', show=True)\n",
|
||||||
" noise_layer = folium.FeatureGroup(name='Data centers: noise / isolated', 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",
|
" 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",
|
" 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",
|
" ).add_to(broadband_layer)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" if SHOW_ELECTION_LAYER and not election_context.empty:\n",
|
" if SHOW_ELECTION_LAYER and SHOW_ELECTION_2020_LAYER and not election_context.empty:\n",
|
||||||
" for row in election_context.dropna(subset=['election_latitude', 'election_longitude']).itertuples(index=False):\n",
|
" e20_rows = election_context.dropna(subset=['election_latitude', 'election_longitude', 'election_2020_rep_margin_pct'])\n",
|
||||||
" margin = getattr(row, 'election_trump_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",
|
" color = election_color(margin)\n",
|
||||||
" radius = max(4, min(11, 4 + abs(float(margin)) / 8.0)) if pd.notna(margin) else 5\n",
|
" radius = max(4, min(11, 4 + abs(float(margin)) / 8.0)) if pd.notna(margin) else 5\n",
|
||||||
" tip = (\n",
|
" tip = (\n",
|
||||||
" f\"Election precinct: {row.election_state_code} {clean_value(row.election_locality)}; \"\n",
|
" f\"Election 2020: {row.election_state_code} {clean_value(row.election_locality)}; \"\n",
|
||||||
" f\"Biden {fmt_number(row.election_biden_share_pct, 1, suffix='%')} / \"\n",
|
" f\"Dem {fmt_number(row.election_2020_dem_share_pct, 1, suffix='%')} / \"\n",
|
||||||
" f\"Trump {fmt_number(row.election_trump_share_pct, 1, suffix='%')}\"\n",
|
" f\"Rep {fmt_number(row.election_2020_rep_share_pct, 1, suffix='%')}\"\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
" folium.CircleMarker(\n",
|
" folium.CircleMarker(\n",
|
||||||
" location=[row.election_latitude, row.election_longitude],\n",
|
" location=[row.election_latitude, row.election_longitude],\n",
|
||||||
@@ -951,10 +967,11 @@
|
|||||||
" color=color,\n",
|
" color=color,\n",
|
||||||
" fill=True,\n",
|
" fill=True,\n",
|
||||||
" fill_color=color,\n",
|
" fill_color=color,\n",
|
||||||
" fill_opacity=0.4,\n",
|
" fill_opacity=0.35,\n",
|
||||||
" weight=1,\n",
|
" weight=1,\n",
|
||||||
" tooltip=tip,\n",
|
" tooltip=tip,\n",
|
||||||
" ).add_to(election_layer)\n",
|
" ).add_to(election_2020_layer)\n",
|
||||||
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
" bounds = []\n",
|
" bounds = []\n",
|
||||||
" for row in points_df.itertuples(index=False):\n",
|
" for row in points_df.itertuples(index=False):\n",
|
||||||
@@ -1005,7 +1022,7 @@
|
|||||||
" opposition_layer.add_to(m)\n",
|
" opposition_layer.add_to(m)\n",
|
||||||
" climate_layer.add_to(m)\n",
|
" climate_layer.add_to(m)\n",
|
||||||
" broadband_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",
|
" clustered_layer.add_to(m)\n",
|
||||||
" noise_layer.add_to(m)\n",
|
" noise_layer.add_to(m)\n",
|
||||||
" centroid_layer.add_to(m)\n",
|
" centroid_layer.add_to(m)\n",
|
||||||
@@ -1035,7 +1052,7 @@
|
|||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"cluster_map.save(MAP_HTML)\n",
|
"cluster_map.save(MAP_HTML)\n",
|
||||||
"print('Wrote:', MAP_HTML.resolve())"
|
"print('Wrote:', MAP_HTML.resolve())\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user