Skip to content

Cell Towers Per Region

Demonstrating a total_network_objects query

In this worked example we use FlowClient to count the number of cell towers per region. This is designed as a simple example to demonstrate that a FlowKit system has been successfully deployed.

The Jupyter notebook for this worked example can be downloaded here, or can be run using the quick start setup.

Load FlowClient and connect to FlowAPI

We start by importing FlowClient. We also import geopandas and mapboxgl, which we will use later to to visualise the data.

import flowclient
import os
import numpy as np
import geopandas as gpd
import mapboxgl
from mapboxgl.utils import create_color_stops

We must next generate a FlowAPI access token using FlowAuth. If you are running this notebook using the quick start setup, generating a token requires the following steps:

  1. Visit the FlowAuth login page at http://localhost:9091.
  2. Log in with username TEST_USER and password DUMMY_PASSWORD.
  3. Under "My Servers", select TEST_SERVER.
  4. Click the + button to create a new token.
  5. Give the new token a name, and click SAVE.
  6. Copy the token string using the COPY button.
  7. Paste the token in this notebook as TOKEN.

The steps are the same in a production setup, but the FlowAuth URL, login details and server name will differ.

Once we have a token, we can start a connection to the FlowAPI system. If you are connecting to FlowAPI over https (recommended) and the system administrator has provided you with an SSL certificate file, you should provide the path to this file as the ssl_certificate argument toflowclient.connect() (in this example, you can set the path in the environment variable SSL_CERTIFICATE_FILE). If you are connecting over http, this argument is not required.

conn = flowclient.connect(
    url=os.getenv("FLOWAPI_URL", "http://localhost:9090"),
    token=TOKEN,
    ssl_certificate=os.getenv("SSL_CERTIFICATE_FILE"),
)

Get tower counts

We can get cell tower counts using a total_network_objects query. We start by creating a specification for a total_network_objects query to count towers per level 3 administrative unit during the first week of 2016.

query_spec = flowclient.aggregates.total_network_objects_spec(
    start_date="2016-01-01",
    end_date="2016-01-08",
    aggregation_unit="admin3",
    total_by="month",
)
query_spec
{'aggregation_unit': 'admin3',
 'end_date': '2016-01-08',
 'event_types': None,
 'geom_table': None,
 'geom_table_join_column': None,
 'hours': None,
 'mapping_table': None,
 'query_kind': 'total_network_objects',
 'start_date': '2016-01-01',
 'subscriber_subset': None,
 'total_by': 'month'}

We run this query using get_result, which returns the result as a pandas DataFrame.

towers_per_admin3 = flowclient.get_result(connection=conn, query_spec=query_spec)
towers_per_admin3.head()
Parts run:   0%|          | 0/6 [00:00<?, ?q/s]
pcod value datetime
0 NPL.1.1.1_1 9 2016-01-01T00:00:00+00:00
1 NPL.1.1.2_1 9 2016-01-01T00:00:00+00:00
2 NPL.1.1.3_1 88 2016-01-01T00:00:00+00:00
3 NPL.1.1.5_1 20 2016-01-01T00:00:00+00:00
4 NPL.1.1.6_1 15 2016-01-01T00:00:00+00:00

Visualise tower counts on a choropleth map

We use the get_geography function to download the geography for the level 3 administrative regions as GeoJSON.

# Download geography data as GeoJSON.
regions = flowclient.get_geography(connection=conn, aggregation_unit="admin3")

# Create a geopandas GeoDataFrame from the GeoJSON
regions_geodataframe = gpd.GeoDataFrame.from_features(regions)

We can now combine the result of the total_network_objects query with the geography data, and use the Mapbox GL library to create a choropleth showing the distribution of cell towers.

Note: Mapbox requires an access token, which should be set as the environment variable MAPBOX_ACCESS_TOKEN. Note that this is only required for producing the Mapbox visualisations, which is completely separate from FlowKit.

towers_per_admin3_geodataframe = (
    regions_geodataframe.join(
        towers_per_admin3.set_index("pcod"), on="pcod", how="left"
    )
    .fillna(value={"value": 0})
    .drop(columns=["centroid", "datetime"])
    .rename(
        columns={
            "pcod": "P-code",
            "value": "Number of towers",
        }
    )
)
mapbox_token = os.environ["MAPBOX_ACCESS_TOKEN"]

# Colour scale for legend
color_stops = create_color_stops(
    np.linspace(1, towers_per_admin3_geodataframe["Number of towers"].max(), 9),
    colors="YlGn",
)

modal_locations_viz = mapboxgl.ChoroplethViz(
    towers_per_admin3_geodataframe.__geo_interface__,
    access_token=mapbox_token,
    color_property="Number of towers",
    color_stops=color_stops,
    opacity=0.8,
    line_color="black",
    line_width=0.5,
    legend_gradient=True,
    legend_layout="horizontal",
    legend_text_numeric_precision=0,
    below_layer="waterway-label",
    center=(84.1, 28.4),
    zoom=5.5,
)

modal_locations_viz.show()