Skip to main content

23 posts tagged with "30DaysOfMaps"

View All Tags

30 Days of Maps Day 13 - A new tool

· 2 min read
James Dales
Co-founder of Tekantis

Day 13 of the #30DayMapChallenge and today's theme is a new tool - Use a tool you’ve never tried before. The challenge has always been about trying new things. Use a tool, software, or drawing technique you’ve never worked with before.

At Tekantis, our focus so far has been on the geospatial visualisation capabilities of Power BI and Microsoft Fabric, but we're getting an increasing number of requests to features that aren't possible to provide purely in the visualisation layer. We've therefore been exploring how to provide some of these more advanced capabilities such as routing and travel times.

As an option I've set up OpenRouteService locally to experiment to see whether it could be an option for providing some of our capabilities. Here's a simple report as a result of some initial testing. The lines in the report represent the fastest driving routes between Heathrow Airport and London City Airport, either as a car or a HGV. I've also included a travel time isochrone representing how far you can drive in an HGV from London City Airport, in 5 minute increments.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 12 - Time and Space

· 3 min read
James Dales
Co-founder of Tekantis

Moving onto day 12 of the #30DayMapChallenge and today's theme is Time and Space - Map something where time matters. Visualize change over time—urban growth, migration, or environmental shifts. Show the relationship between time and geography.

I kind of covered this topic yesterday, showing the ice cap change over time, so I thought I'd pick something that I've had to deal with in customer projects. Boundary changes add a level of complexity to a reporting project, even when there isn't a map. This is a topic that especially impacts public sector organisations. When Local Authorities or Police Forces merge, or when census areas change it creates complexities for comparing data over time. In today's challenge I've attempted to address this. I've brought in crime data from data.police.uk for all of England for the last few years. This covers every police service, for every month. It's about a 4gb dataset before we even start thinking about shapes etc.

I'm mapping the crime data as a choropleth map at Local Authority level, but from here you can drill down to Lower Super Output Area level, and then down to street level as circles. The complexity comes as between 2022 and 2023 there were a number of Local Authority merges in Cumbria, Yorkshire and Somerset. The LSOAs in 2023 aggregate up into different or new Local Authorities.

Firstly in the map, we handle the changes by showing the appropriate shapes for the local authorities according to the date selected in the slicer, and also have separate lookup data between LSOA and local authority for each year. This means the right shapes are showing based on the date, and allows drill-down to continue functioning.

To show some different techniques, I've added the local authority shapes as WKT in the Power BI dataset - this is the easiest method of handling filtering based on date. Then the for the LSOA level, these are all based on 2021 boundaries, so I've used an Esri Shapefile uploaded into the report, and then for the circle layer, I'm using Longitude and Latitude coordinates, and the number of crimes at that location to size the circle. I've also added a filtered reference layer which only shows when circles are showing, to show the boundary of the LSOA we've drilled into.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 11 - Arctic

· 2 min read
James Dales
Co-founder of Tekantis

It's already day 11 of the #30DayMapChallenge and today's theme is the Arctic - Map the Arctic. Whether it’s ice coverage, wildlife habitats, or the effects of climate change, this day is all about mapping the cold extremes of the Arctic..

So mapping the Arctic in Icon Map Pro is a challenge as the Mercator projector excludes the Arctic. I've decided to use the Wiechel projection as it's a top down view of the North Pole. There's a lot of distortion as you move further away from the centre, but as we're focussed on the Pole, it works well.

I'm mapping the extent of the Polar Ice Cap using data from the National Snow and Ice Data Center. I've included the last 10 years of data showing the extent of the ice in December each year - you can drag the slicer show the different years. In addition I've overlaid the Arctic sea routes from the National Geospatial Intelligence Agency.

To draw the Wiechel projection, I've used the same technique as I did for the US Elections using the Albers USA projection, except this time I discovered the Dirty Reprojectors online app which allows you to upload a GeoJSON file in the WGS84 projection and it maps the coordinates to your selected projection - but using the WGS84 coordinate system. This means that the features appear centered in the middle of the map - not in their correct locations, so you can't use a background map. Instead of the background map, I'm using a geoJSON overlay of the countries - in the Wiechel projection.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 10 - Pen & Paper

· 2 min read
James Dales
Co-founder of Tekantis

We're onto day 10 of the #30DayMapChallenge and the theme is pen and paper - Draw a map by hand. Go analog and draw a map using pen and paper. The result doesn’t have to be perfect—it’s about the creative process.

Now this one is a real challenge as I'm no artist! As a child I loved reading the Swallows & Amazons series of books by Arthur Ransome. One of the things I loved about the books was they always contained a map showing where the different aspects of the story were set - whether in the real world, or fictional world - and sometimes somewhere in between.

Whilst the challenge is pen and paper, this doesn't mean we can't visualise the end result in Icon Map Pro and use all the same interactivity capabilities we would expect from a normal map...

I've taken inspiration from Swallows and Amazons and drawn a map of the fictional lake in the Lake District where some of the books were set. I've hand drawn it, scanned it in, and used it as a custom image background in Icon Map Pro. I then drew a small sailing dinghy, and used a online tool to convert it to a 64bit encoded data URL which I've added as a DAX measure into the Power BI report. I've then traced a path from one side of the lake to the other around some of the islands, and used the Play Axis custom visual to animate the journey of the boat across the lake. I've made a DAX measure to half-calculate the rotation angle of the boat to add to the effect.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 9 - AI Only

· 8 min read
Brynn Borton
Co-founder of Tekantis

We're onto day 9 of the #30DayMapChallenge.

The theme for today's map is AI Only - This day is all about prompt engineering. Use AI tools like DALL-E, MidJourney, Stable Diffusion, or ChatGPT with geospatial capabilities to create a map based on AI-generated content. The challenge is to get the right prompt and critically assess the output—how well does AI capture or distort the map's intent?

I thought I would explore a scenario where you're working in a development environment with sensitive customer data but need substantial dummy data to test your reports. Using real customer data can be fraught with legal and privacy issues, especially in industries like finance, healthcare, and retail. That's where synthetic data comes in handy. Luckily, at Tekantis, we're already actively exploring synthetic data generation using OpenAI as part of one of our sideline lab projects. We have developed a Synthetic Data Generator Custom GTP to help create realistic but fake datasets. I have leveraged this tool to generate synthetic customer data with geospatial attributes to test map visualisation in Power BI.

The Business Scenario

Imagine you work in a company that handles customer data for market analysis. You need to create visual reports, but you can't use actual customer data because of privacy policies. Instead, I generated a dataset of 20,000 synthetic customer records, including attributes like customer names, age, gender, occupations, phone numbers, and coordinates within London. To make the data more realistic, we clustered customers around specific areas of London, mimicking natural customer distributions.

The Result

So how did we do it?

The Synthetic Data Generator Custom GTP 

The Synthetic Data Generator Custom GTP is designed to act as a multi-step process, working with the user interactively. First, it clarifies each requirement before moving on to the next step, ensuring accuracy and alignment with user expectations. Then, it uses the code interpreter to generate and execute Python code in real-time.   Once the data is successfully generated, it provides a downloadable link for easy access to the resulting file.

The first step is for the AI and User to agree the plan, the GTP does most of the work here I simply prompted with the above scenario and after a couple of iterations the GTP had generated the following plan:

  1. Bounding Box over London: Use a simple square bounding box covering London to generate random latitude and longitude coordinates, simplifying the location generation process.

  2. Attributes to Include: Generate a dataset of 20,000 customer records with the following attributes:

    • Customer ID: Unique identifier for each customer.
    • Name: Generated using gender-specific names from Faker.
    • Gender: Evenly split between male, female, and a small percentage for other categories.
    • Age: Distributed between 18 and 80 years, with more concentration in the 25-55 range.
    • Occupation: Realistic job titles generated by Faker.
    • Income: Random annual income between £20,000 and £100,000.
    • Phone Number: UK-based phone numbers.
    • Latitude and Longitude: Randomly generated coordinates within London.
    • Life Time Value (LTV): A randomised total sales figure for each customer.
  3. Clustering Data: Create clusters of customer locations to simulate areas with higher customer density:

    • Defined five cluster centres across London: Central, North, East, West, and South.
    • 80% of data points will be clustered around these centres.
    • 20% will be uniformly distributed to simulate a spread-out population.
  4. Data Generation Process:

    • Generate clustered data points using normal distributions centred around each cluster centre.
    • Generate uniform data points to cover the remainder of London.
    • Generate demographic data (e.g., name, age, occupation) using the Faker library.
  5. Validation:

    • Ensure all latitude and longitude values fall within the bounding box.
    • Verify that gender-specific names align correctly.
    • Ensure age and income distributions are realistic.
  6. Output: Generate a CSV file with all attributes included and provide a download link for easy access.

The second step is to generate and run the Python code, this is the code it produced which actually worked first time.  However, even if it hadn't the beauty with the ChatGTP code interpreter is that it can identify and self-correct errors in the code, rerunning the script until it executes successfully:  

import random
import pandas as pd
from faker import Faker
import numpy as np

# Initialize Faker
fake = Faker('en_GB') # Use 'en_GB' locale for UK-specific data
Faker.seed(42)
random.seed(42)
np.random.seed(42)

# Parameters
num_records = 20000

# Define the bounding box over London (approximate coordinates)
min_lat, max_lat = 51.2868, 51.6919
min_lon, max_lon = -0.5103, 0.3340

# Cluster centers (latitude and longitude)
cluster_centers = [
(51.5074, -0.1278), # Central London
(51.5555, -0.0786), # North London
(51.4860, -0.0200), # East London
(51.5121, -0.3007), # West London
(51.4400, -0.0550), # South London
]

# Number of clusters and distribution of points
num_clusters = len(cluster_centers)
cluster_size = int(0.8 * num_records / num_clusters)
uniform_size = num_records - (cluster_size * num_clusters)

# Function to generate clustered points
def generate_clustered_points(center, num_points, cluster_std=0.02):
lat_center, lon_center = center
latitudes = np.random.normal(loc=lat_center, scale=cluster_std, size=num_points)
longitudes = np.random.normal(loc=lon_center, scale=cluster_std, size=num_points)
return latitudes, longitudes

# Generate clustered data
latitudes = []
longitudes = []
for center in cluster_centers:
lat, lon = generate_clustered_points(center, cluster_size)
latitudes.extend(lat)
longitudes.extend(lon)

# Generate uniformly distributed data
uniform_latitudes = np.random.uniform(min_lat, max_lat, uniform_size)
uniform_longitudes = np.random.uniform(min_lon, max_lon, uniform_size)
latitudes.extend(uniform_latitudes)
longitudes.extend(uniform_longitudes)

# Truncate or extend lists to match num_records
latitudes = latitudes[:num_records]
longitudes = longitudes[:num_records]

# Generate demographic data
genders = np.random.choice(['Male', 'Female', 'Other'], size=num_records, p=[0.49, 0.49, 0.02])

names = []
ages = []
occupations = []
incomes = []
phone_numbers = []
customer_ids = []
lifetime_values = []

for i in range(num_records):
gender = genders[i]
if gender == 'Male':
name = fake.name_male()
elif gender == 'Female':
name = fake.name_female()
else:
name = fake.name_nonbinary()
names.append(name)

# Age distribution (more customers in the 25-55 age range)
age = int(np.random.normal(loc=40, scale=12))
age = max(18, min(age, 80)) # Clamp age between 18 and 80
ages.append(age)

# Occupation
occupation = fake.job()
occupations.append(occupation)

# Income level based on age (simplified assumption)
income = random.randint(20000, 100000)
incomes.append(income)

# Phone number
phone_number = fake.phone_number()
phone_numbers.append(phone_number)

# Customer ID
customer_id = fake.unique.uuid4()
customer_ids.append(customer_id)

# Life Time Value
lifetime_value = round(random.uniform(500, 50000), 2)
lifetime_values.append(lifetime_value)

# Create the DataFrame
data = {
'CustomerID': customer_ids,
'Name': names,
'Gender': genders,
'Age': ages,
'Occupation': occupations,
'Income': incomes,
'PhoneNumber': phone_numbers,
'Latitude': latitudes,
'Longitude': longitudes,
'LifeTimeValue': lifetime_values
}

df = pd.DataFrame(data)

# Save to CSV
file_path = 'synthetic_customer_data_with_ltv.csv'
df.to_csv(file_path, index=False)

The GTP also provided a nice sample of the Resulting Dataset before asking if we want to execute the whole process. You can see here that using Faker it has generated realistic name, phone numbers, occupations etc.

CustomerIDNameGenderAgeOccupationIncomePhoneNumberLatitudeLongitudeLifeTimeValue
6f9c5f25-4e88-4e28-b2c5-9fca56642d4cStephen ArnoldMale36Producer, radio83265020 2012 266051.5179-0.114735264.12
b04a0f2b-4912-4863-855e-dcc1127b48fbSusan GibsonFemale38Pensions consultant70749020 0908 986451.5186-0.132612634.56
0ef57f34-1062-44a8-95cf-d8c3c976dc0eEwan HurstMale49Psychologist, educational24137016977 292451.5255-0.11628849.73

The final step is to execute the full process generating a nice download link for me to grab the file.   

Final Output

Loading into Power BI and Visualising

Once the data was generated, I loaded the CSV file directly into Power BI to visualise it. To my delight, it worked flawlessly on the first try! All 20,000 latitude and longitude points appeared perfectly within the bounding box of London, and the clusters were visibly centred in the areas I defined earlier, providing a realistic spread of customer data across different parts of the city.

Using Power BI's mapping capabilities, I could see the density of customers in the different clusters I had created, which mimicked the likely distribution of customer hotspots. This is an excellent way to simulate and test a customer analysis report without risking any sensitive information.

Conclusion and Next Steps

This is, of course, a very basic example, and we are somewhat limited to the Python libraries available within the standard ChatGPT environment. However, at Tekantis, we are working on a custom version of our Synthetic Data Generator that will offer far more extensibility, including additional libraries and more sophisticated data generation capabilities.

Stay tuned for future updates—exciting possibilities are on the horizon!

Final Write Up

Keeping to today's theme I also used the new "GTP-4o with canvas" tool to generate this blog!

If you'd like to see how the report was built, you can download it here.

You can also have a play with our Synthetic Data Generator here

30 Days of Maps Day 8 - Humanitarian Data Exchange (HDX)

· 2 min read
James Dales
Co-founder of Tekantis

We're onto day 8 of the #30DayMapChallenge.

The theme for today's map is Humanitarian Data Exchange (HDX) - *Use data from HDX to map humanitarian topics. Explore the datasets from the Humanitarian Data Exchange, covering disaster response, health, population, and development. Map for social good. *

The dataset caught my eye as it's vast! I'm using the Global Population Density for 400m H3 Hexagons dataset from Kontur. I then limited to the Central American countries which is still 286,422 rows of data. As we've seen from previous challenges, more than 30,000 rows is a challenge for Power BI. I've used a forthcoming version of Icon Map Pro that will enable more H3 hexagons to be displayed on the map - in this case nearly 300,000.

The earlier challenge where I used H3 hexagons, Icon Map Pro generated the H3 cells based on aggregating longitude and latitude coordinates, whereas in this case, the data is provided as 2 columns - H3 Cell Index and Population in that cell. Icon Map Pro natively supports these cell IDs so the map configuration is really easy and only requires these 2 fields. I also overlaid a shape file with the country outlines in too, and added a Power BI slicer to zoom to specific countries.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 7 - Vintage style

· 2 min read
James Dales
Co-founder of Tekantis

Today is day 7 of the #30DayMapChallenge.

The theme for today's map is Vintage style - Map something modern in a vintage aesthetic. Create a map that captures the look and feel of historical cartography but focuses on a contemporary topic. Use muted colors, fonts, and classic elements.

There were a number of ways I considered tackling this challenge. The first was to head over to Mapbox Studio as Icon Map Pro supports Mapbox Studio styles. However, I thought it would be good to see what I could achieve without relying on third-party integrations. So instead I've achieved the vintage style by firstly placing an image of a canvas as the background for my Power BI report. I generated this using Chat GPT. I then added Icon Map Pro to the report and set the visual background to Off. This doesn't immediately have any effect, but in the background map options, you can change the transparency. So I chose our monochrome "Toner (no labels)" style and set its transparency to around 70 percent. That means that the normally white areas of the map show the map canvas, and the black water and infrastructure areas are shown in a dark brown. I feel this gives a nice vintage appearance.

Then on top of this I added a feature layer of listed buildings from Historic England's Open Data Hub. This is loaded directly from their servers using our ArcGIS integration and displayed in a transparent blue colour, so you still get some of the canvas effect. Then finally I applied tooltips - data sourced directly out of the ArcGIS layer to show the name of the building. I chose a serif style font, and set the transparency of the tooltip so the canvas shows through a little.

Finally I added the London Boroughs as a shape layer, so it's possible to zoom into a specific London Borough using a Power BI slicer - which again through transparency options on the shape layer is highlighted on the map.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 6 - Raster

· One min read
James Dales
Co-founder of Tekantis

Today is day 6 of the #30DayMapChallenge.

The theme for today's map is raster - A map using raster data. Rasters are everywhere, but today’s focus is purely on grids and pixels—satellite imagery, heatmaps, or any continuous surface data.

Whilst most data displayed on the map in Power BI is going to be point or vector data bound to the Power BI data model, it is often useful to add additional reference information in the form of raster data. Icon Map Pro supports a number of sources of raster data including XYZ tiles and WMS layers.

In this report we've overlaid data from the British Geological Survey (BGS) on top of the base map. The Power BI slicer in the top right corner allows you to select from a number of different layers detailing the makeup of the UK's bedrock. To add some additional information, we've overlaid a GeoJSON layer showing the locations of all the UK's quarries.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 5 - A journey

· 2 min read
James Dales
Co-founder of Tekantis

Today is day 5 of the #30DayMapChallenge.

The theme for today's map is a journey - Map any journey. Personal or not. Trace a journey—this could be a daily commute, a long-distance trip, or something from history. The key is to map movement from one place to another.

The data for today's map comes from aircraft flying above my house. I capture the transponder transmissions using a Raspberry PI with an antenna and push these into Microsoft Fabric's Realtime Intelligence workload. I've included a morning's worth of captured data from November 1st, and animated it using the Play Axis custom visual.

The data captured includes the longitude and latitude information for each aircraft - these are stored on a separate row in the dataset. I then use the CONCATENATEX function in DAX to generate a Well-Known Text (WKT) linestring. Using the aircraft's unique ID as the ID field in Icon Map Pro, and then the DAX measure, it create a WKT linestring for each aircraft. I'm showing the last 10 minutes worth of history for each aircraft - so you'll see planes not moving for a while after I stop detecting data for them. At the end of each linestring, I'm displaying an SVG image of an aircraft - different images for each category of plane. These images are rotated within Icon Map Pro to point in the direction of travel.

If you'd like to see how the report was built, you can download it here.

30 Days of Maps Day 4 - Hexagons

· 2 min read
James Dales
Co-founder of Tekantis

Today is day 4 of the #30DayMapChallenge.

The theme for today's map is hexagons - Maps using hexagonal grids. Step away from square grids and try mapping with hexagons. A fun way to show density or spatial patterns..

Given our logo, this challenge could have been made for us! Icon Map Pro has the in-built capability to index point data (longitudes and latitudes) into Uber's H3 hexagon grid system - as well as providing support for data already indexed. For this challenge we're going to use open data from data.police.uk and examine every crime committed in England in September 2024. It turns out there were over 435,000 crimes committed in England in September that had their location recorded. In fact there were nearly different 200,000 locations in the dataset.

This report is sending all 435,000 crimes into the map to be converted into hexagons - well beyond the data limits for the "out of the box" visuals. The report allows you to select the resolution for these hexagons using a Power BI slicer. Pick a higher number for smaller hexagons when zooming in an looking at a smaller area. The hexagon cells are coloured according to how many crimes were recorded within that area - from white being the lowest through blue to red being the highest (with more time available I'd have added a legend). Two Power BI bar charts allow you to filter the map by type of crime and region, and a slicer is provided to drill into specific Local Authorities. You can also click on a cell to see the breakdown of crimes within each cell.

If you'd like to see how the report was built, you can download it here.

Includes data from data.police.uk and ONS GeoPortal. All the data on this site is made available under the Open Government Licence v3.0.