Building a WiFi spots Interactive Map of Networks around you with WiGLE and Python

Random Nerds

Recently one of my LinkedIn connections shared an article on generating this Map of Networks with WiGLE and R, and Python enthusiasts inquisitively inquired if this can be achieved with Python as well. So let us try to get this done for Pune as well as Bangalore location!

Let us now move ahead and replicate his work in Python. We are going to identify the open WiFi Networks around us and map them elegantly in order to visualize. Now there is a Catch-22 here but let me not jump the gun, and let us wait for it till the end. We wait for an entire year to watch next season of GoT, so I guess another 3–4 minutes is a fair ask. To start with, we need to ensure we’ve following packages in our Toolkit and if you don’t, then just create a virtual environment & follow instructions shown below:

  • requests : To send HTTP requests because sometimes WiGLE authentication process gets really wiggly and throws bunch of errors.
  • WiGLE API : To search & collect data from their database.
  • Pandas : To analyze and manipulate data.
  • Geoplotlib : To visualize our WiFi Network results (you can use leaflet as well, but then list of alternatives are endless).

Again a big shout-out to all those AWESOME contributors who do so much for our open-source community by developing & maintaining such brilliant packages. THANK YOU GUYS!


By now, I believe you already have requests, pandas and geoplotlib installed. So let us start by importing these libraries:

# IMPORTING REQUIRED LIBRARIES:
import requests
from requests.auth import HTTPBasicAuth
import pandas as pd
from pandas.io.json import json_normalize
import matplotlib.pyplot as plt
import geoplotlib as gp

Meanwhile, let us also head to our WiGLE account, and make a note of our authentication Username and Password. Ensure to sign in with your credentials, click on ‘Tools’ in the top navigation bar, and then select Account, that shall look like:

WiGLE Nav bar → Tools → Account

Next page shall have a general End User License Agreement, where you need to click on ‘Show my token’. From all the information displayed on the very next page, we need to safely copy

  • API Name that we will use as our Username, and
  • API Token that we shall use as our Password.

By the way, while importing geoplotlib, if you receive any errors regarding pyglet, then install that library as well, using pip install pyglet . Also our target location in this illustration is Pune (Maharashtra, India) but if you want it to reflect your city, just change the name in below code (and associated details) for it to work. With that, we’re good to start writing our actual code:

# SETTING WiGLE USERNAME & PASSWORD FOR API CALL:
wigle_username = 'enter_your_api_name_here'
wigle_password = 'enter_your_api_token_here'
# SETTING PARAMETERS:
payload = {'first': '0', 'freenet': 'false', 'paynet': 'false', 'addresscode': 'Pune', 'api_key': (wigle_username + wigle_password).encode()}
# FETCHING JSON RESPONSE FROM WiGLE:
pune_details = requests.get(url='https://api.wigle.net/api/v2/network/geocode', params=payload, auth=HTTPBasicAuth(wigle_username, wigle_password)).json()
# PRINTING TO CHECK RESPONSE:
print(pune_details)

So our hit was successful and we got a response from which we shall make a note of Pune Bounding Box, shown as a combination of latitudes and longitudes within boundingbox scope. Response shall look like a typical JSON output:

WiGLE geocode response for Pune

Now we shall use this boundingbox values within our payload , as WiGLE Search acceptable parameters to fetch 100 similar results for this illustration. These parameters are latrange1, latrange2, longrange1 & longrange2. Note that by default, COMMAPI queries are bounded at 100 pages but if you want more data out of it, alter first parameter within your payload :

# SETTING PARAMETERS:
payload = {'latrange1':'18.3603062', 'latrange2':'18.6803062', 'longrange1':'73.6943185', 'longrange2':'74.0143185', 'api_key': (wigle_username + wigle_password).encode()}
# FETCHING JSON RESPONSE FROM WiGLE:
pune_results = requests.get(url='https://api.wigle.net/api/v2/network/search', params=payload, auth=HTTPBasicAuth(wigle_username, wigle_password)).json()
# PRINTING TO CHECK PUNE RESULTS:
print(pune_results)

As desired, entire list of 100 nearby WiFi SSIDs with many other relevant details are right in front of us, but details that catch our attention here are the coordinates in trilat and trilong . I sincerely apologize to those who thought we gonna get passwords to the secure/encrypted networks, but apart from that, everything else right there.

100 nearby WiFi Spots

Let us quickly get these responses converted into a human readable format, so let’s take help from mighty Pandas for a JSON conversion to DataFrame. Also let us perform little column renaming & then preview outcome:

# EXTRACTING 'RESULTS' AS A PANDAS DATAFRAME TO WORK WITH:
df = json_normalize(pune_results['results'])
# RENAMING COLUMNS FOR GEOPLOTLIB:
df = df.rename(columns={'trilat': 'lat', 'trilong': 'lon'})
cols = list(df.columns)
# PREVIEWING AVAILABLE INFORMATION:
print(f"Result obtained has {df.shape[0]} rows and {df.shape[1]} columns in it. \n\nThe list of columns include {cols}")
print(df)

geoplotlib expects input DataFrame to contain location co-ordinates as lat and lon columns, and rest of data gets automatically shaped up. We had it as trilat and trilong , hence needed to rename those columns.

As far as data is concerned, we already have what we needed. So without further adieu, let us plot it as well:

# GENERATING WIFI SPOTS MAP OF NETWORKS:
gp.dot(df)
gp.show()

And yes! That is all the code we need, to create a (default) static visualization on top of OpenStreetMap theme (Tiles), that shall appear as:

Static WiFi Spots Map of Networks with Geoplotlib

There are multiple other Themes to be chosen from! With all of that done, now let us discuss our Catch-22, which actually you would’ve guessed by now. The plot we have here is static, but what if we want it to be interactive? Well geoplotlib also offers custom options like interactivity, colors, tiles, etc. To play further with this map, you can import rest of the components as:

# FOR INTERACTIVITY & OTHER OPTIONS WITH GEOPLOTLIB:
from geoplotlib.layers import BaseLayer
from geoplotlib.core import BatchPainter
from geoplotlib.colors import colorbrewer
from geoplotlib.utils import epoch_to_str, BoundingBox

Then create a CustomClass based on their BaseLayer to make it even more appealing. In fact, they have a very nice and intuitive User Guide as well that you may like to refer to. If someone extends further to make this visualization interactive, please do share a link in the Comments section below for other learners to benefit.


Meanwhile if that wait doesn’t sound justified, then let us look at a piece of work by superhero Abdul, and he has all the code ready in his Notebook to make plot interactive as well. And the fun doesn’t end there because we also get to learn another fabulous geoplotting library, Folium. Let us follow along to comprehend to what he tries to achieve. So let us begin by importing the library and then make an addition to our current Pandas DataFrame df. In case you don’t have Folium already installed in your environment, all you need to do is pip install folium.

# IMPORT LIBRARY:
import folium
# CREATING AN EMPTY NEW COLUMN FOR FOLIUM TO LATER USE:
df['color'] = ''
# PICKING 'UNKNOWN' & 'NONE' VALUES FROM 'ENCRYPTION' COLUMN TO FEED 'COLOR' COLUMN:
df['color'] = df['encryption'].map({'unknown':'yellow', 'none':'red', 'wep':'blue', 'wpa':'blue', 'wpa2':'blue'})
# VERIFYING 'ENCRYPTION' & 'COLOR' COLUMNS IN OUR DATAFRAME:
df[['encryption', 'color']].tail(10)

With our Geoplotlib output, we noticed solid red markers for all the 100 SSIDs on our map. Although not mandatory, we plan to add a column named color that shall cluster all SSIDs based on the network encryption type. There are 5 of them in our encryption column of df DataFrame, namely wep , wpa, wpa2, unknown and none. So we assigned different colors to known & unknown encryption types of network. And that fetches us:

Assignment of ‘color’ column to our DataFrame

With color column in place, we shall now set a Base map for Pune location, and for a change, let us modify the default theme/tile of OpenStreetMap to cartodbdark_matter. Once that is done, we shall plot all 100 records clustered in differently colored circle markers on our Basemap. Finally, we shall also save a local copy of it. Note that this copy will get saved in the current working directory of your IDE. If you’re working on Jupyter Notebook, run pwd to know your directory.

# SETTING BASE MAP FOR PUNE WITH A THEME:
pune_map = folium.Map([18.3603062, 74.0143185], zoom_start=6, tiles='cartodbdark_matter')
# USING 'COLORS' COLUMN TO SEGREGATE 100 SSIDs
# WITH CIRCLE MARKS, AND ALSO ADD A POPUP MESSAGE:
df.apply(lambda row: folium.CircleMarker(location= [row["lat"], row["lon"]], radius= 5, color= row['color'], popup = "SSID: %s /n Encrpytion %s" %(row["ssid"],row["encryption"])).add_to(pune_map), axis=1)
# CHECKING OUTCOME & SAVING A COPY OF MAP:
pune_map.save('Pune Map')
pune_map

Still there are many other options that can be applied to enhance aesthetics, like various markers, filling in colors within those markers, etc. For example, to fill in color to our markers on plot, we could have just added to our .CircleMaker() another parameter as fill=True (by default, it is set to False) and fill_color=’your color_choice’ . And all of these efforts shall lead to an Output pretty much like:

https://vimeo.com/user95440991/review/319149844/38ddba0335

Appreciate your time and patience guys! Enjoy Learning. :)

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade