Mapping Internet Coverage#

Prepare Geography#

Create H3 grid shapefile which has a column for admin regions 1 to 2

Preprocess Data#

Explore Data and Do Some Cleaning#

To address extreme values, which we classify as outliers for this analysis, we apply clipping. Specifically, we remove all values for upload speed, download speed, and latency that exceed the 95th percentile.

Mapping Internet Perfomance#

Merge with geography-H3#

Map of Performance As of July 31, 2024#

Hide code cell source

# Mobile network performance
message = " MOBILE NETWORK PERFORMANCE (DOWNLOAD, MBPS)(JAN-JULY 2024)"
map_network_performance(gdf=gdf_mob_sum_jul, description=message)
================================================================================
  MOBILE NETWORK PERFORMANCE (DOWNLOAD, MBPS)(JAN-JULY 2024)
================================================================================
../../_images/57cc60553be96bb422f907e034e0d818a9daf470aad0cc2aa70407d986bc11a2.png

Hide code cell source

# Mobile network performance
message = " FIXED NETWORK PERFORMANCE (DOWNLOAD, MBPS)(JAN-JULY 2024)"
map_network_performance(gdf=gdf_fixed_sum_jul, description=message)
================================================================================
  FIXED NETWORK PERFORMANCE (DOWNLOAD, MBPS)(JAN-JULY 2024)
================================================================================
../../_images/14b54e250d59478e169003f680ef8048ffb7305e35d93d180e2324a9ac6f7611.png

Test Counts in July, 2024#

Hide code cell source

col_name = 'test_id_count'
gdf = gpd.GeoDataFrame(gdf_mob_sum_po, geometry='geometry')
gdf['date'] = gdf['date'].dt.to_timestamp()
gdf.rename(columns={'h3_index_': 'h3_index'}, inplace=True)

# Create a base map
m = folium.Map(location=[gdf.geometry.centroid.y.mean(), gdf.geometry.centroid.x.mean()], zoom_start=10)

# Create a color scale
# Clip data to 5th and 95th percentiles
# lower_bound = np.percentile(gdf['activity_index_sum'], 5)
upper_bound = np.percentile(gdf[col_name], 95)

colormap = cm.LinearColormap(
    colors=['#ffffcc', '#ffcc66', '#ff6600', '#cc0000'],
    vmin=gdf[col_name].min(),
    vmax=upper_bound
)


colormap.caption = 'Test Count'
colormap.add_to(m)

# Prepare features for TimestampedGeoJson
features = []
for idx, row in gdf.iterrows():
    color = colormap(row[col_name])
    feature = {
        'type': 'Feature',
        'geometry': row['geometry'].__geo_interface__,
        'properties': {
            'time': row['date'].date().__str__(),
            'style': {
                'color': 'black',
                'fillColor': color,
                'fillOpacity': 0.7,
                'weight': 1,
                'opacity': 0.9
            },
            'id': row['h3_index'],
            col_name: row[col_name],
            'tooltip': f"H3 Index: {row['h3_index']}<br>Test Count: {row[col_name]}"
        }
    }
    features.append(feature)

# Add TimestampedGeoJson to the map
TimestampedGeoJson(
    {'type': 'FeatureCollection', 'features': features},
    period='P1D',
    add_last_point=True,
    auto_play=False,
    loop=False,
    max_speed=0.2,
    loop_button=True,
    date_options='YYYY/MM/DD',
    time_slider_drag_update=True
).add_to(m)

# Add administrative boundaries
gdf_adm = gdf_adm2[['ADM2_EN', 'geometry']]
folium.GeoJson(
    gdf_adm,
    name='Admin2 boundary',
    style_function=lambda x: {
        'color': '#808080',
        'weight': 2,
        'fillOpacity': 0
    }
).add_to(m)


# Add layer control
folium.LayerControl().add_to(m)

# Save and display the map
output_fp = DIR_OUTPUTS.joinpath('test-count-jul-sept2024.html')
m.save(str(output_fp))
m  # Display in Jupyter Notebook (if applicable)
Make this Notebook Trusted to load map: File -> Trust Notebook

Hide code cell source

col_name = 'test_id_count'

# ✅ Get unique regions
regions = gdf["ADM1_EN"].unique()

# ✅ Create tab contents
tab_contents = []
tab_names = []

for region in regions:
    # ✅ Filter data for the specific region
    gdf_region = gdf[gdf["ADM1_EN"] == region]

    # ✅ Create a base map centered on the region
    m = folium.Map(
        location=[gdf_region.geometry.centroid.y.mean(), gdf_region.geometry.centroid.x.mean()], 
        zoom_start=10
    )

    # ✅ Create a color scale (adjusting for distribution)
    upper_bound = np.percentile(gdf_region[col_name], 95)

    colormap = cm.LinearColormap(
        colors=['#ffffcc', '#ffcc66', '#ff6600', '#cc0000'],
        vmin=gdf_region[col_name].min(),
        vmax=upper_bound
    )

    colormap.caption = 'Test Count'
    colormap.add_to(m)

    # ✅ Prepare features for TimestampedGeoJson
    features = []
    for _, row in gdf_region.iterrows():
        color = colormap(row[col_name])
        feature = {
            'type': 'Feature',
            'geometry': row['geometry'].__geo_interface__,
            'properties': {
                'time': row['date'].date().__str__(),
                'style': {
                    'color': 'black',
                    'fillColor': color,
                    'fillOpacity': 0.7,
                    'weight': 1,
                    'opacity': 0.9
                },
                'id': row['h3_index'],
                col_name: row[col_name],
                'tooltip': f"H3 Index: {row['h3_index']}<br>Test Count: {row[col_name]}"
            }
        }
        features.append(feature)

    # ✅ Add TimestampedGeoJson to the map
    TimestampedGeoJson(
        {'type': 'FeatureCollection', 'features': features},
        period='P1D',
        add_last_point=False,
        auto_play=False,
        loop=False,
        max_speed=0.2,
        loop_button=True,
        date_options='YYYY/MM/DD',
        time_slider_drag_update=True
    ).add_to(m)

    
    # ✅ Add layer control
    folium.LayerControl().add_to(m)

    # ✅ Save and display the map
    output_fp = DIR_OUTPUTS.joinpath(f'test-count-{region}.html')
    m.save(str(output_fp))

    # ✅ Create an output widget for the region
    output_widget = widgets.Output()
    with output_widget:
        display(m)

    # ✅ Append to tab contents
    tab_contents.append(output_widget)
    tab_names.append(region)

# ✅ Create interactive tabs
tabs = widgets.Tab(children=tab_contents)

# ✅ Assign tab titles correctly
for i, name in enumerate(tab_names):
    tabs.set_title(i, name)

# ✅ Display tabs in Jupyter Notebook
display(tabs)