The search for the two closest two tram stops in a network full of pointless tram stops.
29 November 2025
Ava Vu
The search for the two closest two tram stops in a network full of pointless tram stops.
When I travel on the Route 70 toward Glenferrie road, there’s a moment that always trips me up. I cruise past Kooyongkoot Road, and in preparation for my stop I pull the string. The 70 only runs the old style trams with the string, so I make sure to savour it before the old trams get phased out.
But as I am mid string-pull-savour, I’m snapped back into reality. An announcement comes over the loud speaker: “Now arriving at Berkeley Street”. I’ve just requested one stop early! And now I’m going to be late to get frozen yoghurt on Glenferrie Road.
Remarkably, the distance between Berkeley Street to Kooyongkoot Road is just 116 metres. This is a tiny distance, better travelled on foot than by tram. With both stops serving the same chunk of unassuming Inner East suburbia, this very well could be the most useless tram stop in Melbourne. But is it? How can we find out? Per-stop ridership data is not available, so to figure out the most useless stop let’s go with a helpful proxy: which two Melbourne tram stops are closest together?
This is a gripping question that has been eating away at me for a few months now. But then one day, I woke up and remembered I know how to code. Thinking about the problem, it really didn’t seem that hard. And it wasn’t. I finished most of the work in an afternoon.
Here’s the top three:
| Rank | Name | Distance | Link |
|---|---|---|---|
| 1. | Miller Street to St Georges Road | 62.50m | Google Maps |
![]() | |||
| 2. | Hawthorn Road to Dandenong Road | 72.09m | Google Maps |
![]() | |||
| 3. | Caulfield Station to Dandenong Road | 77.49m | Google Maps |
![]() |
And the answer is Miller Street to St Georges Road! At just 62 metres, you could likely get off at Miller Street, run to St Georges Road, and get on the same tram. Or at least you could, if it wasn’t for the large intersection requiring prospective passengers to wait at the pedestrian crossing. Closeness is not quite the same thing as usefulness. This stop allows passangers to skip waiting for the light to turn green. It’s not super important, but removing it would make someone’s life slightly worse.
That’s also true for Hawthorn Road to Dandenong Road, also positioned on a T-intersection. These stops are arguably useful, and are arguably less close than my code would make it seem as I only took distances as the crow flies, meaning a straight line. Because these stops are positioned at either side of an intersection, actual passengers would be required to walk further. You can watch YouTuber Qazzy ride this journey in real time.
The first truly useless stop is at number 3: Caulfield Station to Dandenong Road. This stop connects Caulfield Station to nothing, and was my prediction for the closest two tram stops. At 77 metres, this stop really has no reason to exist. For some context, the network average is 285 metres. Pulled from Distributions of walking access to public transport in Melbourne, Australia – Evidence on acceptable and tolerable walking distances You can walk the distance in 30 seconds.
Apparently, there used to be more of these useless stops around the network, but the government has been slowly cleaning them up as they make way for low floor trams. It would be an interesting sub-question to find the shortest distance between tram stops to ever exist on the network. That can be homework for someone else.
So there it is, Caulfield Station to Dandenong Road is my pick for the most useless tram stop.
Here’s the full bottom 15. Berkeley Street to Kooyongkoot Road, my local stops that started me on this quest, ranks 12.
| Rank | Name | Distance | Link |
|---|---|---|---|
| 1. | Miller Street to St Georges Road | 62.50m | Google Maps |
![]() | |||
| 2. | Hawthorn Road to Dandenong Road | 72.09m | Google Maps |
![]() | |||
| 3. | Caulfield Station to Dandenong Road | 77.49m | Google Maps |
![]() | |||
| 4. | McNamara Street to Regent Street | 91.44m | Google Maps |
![]() | |||
| 5. | Camberwell Junction to Camberwell Market | 96.09m | Google Maps |
![]() | |||
| 6. | Brighton Road to St Kilda Town Hall | 99.92m | Google Maps |
![]() | |||
| 7. | Kew Shopping Centre to High Street | 101.05m | Google Maps |
![]() | |||
| 8. | St Kilda Station to Canterbury Road | 103.78m | Google Maps |
![]() | |||
| 9. | Kooyong Tennis Centre to Kooyong Station | 110.22m | Google Maps |
![]() | |||
| 10. | North Melbourne Town Hall to Curzon Street | 111.86m | Google Maps |
![]() | |||
| 11. | Glenhuntly Shops to Glen Huntly Station | 115.24m | Google Maps |
![]() | |||
| 12. | Berkeley Street to Kooyongkoot Road | 116.44m | Google Maps |
![]() | |||
| 13. | Malvern Station to Hawthorn Road | 117.66m | Google Maps |
![]() | |||
| 14. | Footscray Market to Footscray Station | 120.17m | Google Maps |
![]() | |||
| 15. | Footscray Market to Nicholson Street | 122.80m | Google Maps |
![]() |
If you’re a nerd, you’re probably wondering how I made this. This project was partly inspired by me learning about the closest pairs problem, a common textbook problem for teaching algorithmic complexity. How do you find the closest two pairs of points in a list of points? The naïve solution is just comparing every point to every other point, which means doing (Number of points)² calculations.
Melbourne has around 1700 tram stops, which means 1700², which is 2,890,000 calculations. There is a canonical improvement to the closest pair problem that splits up the points into smaller sections. That solves the problem in (Number of points) * log(Number of points) which is (roughly) 18,200 comparisons.
Two million is a large number, and I was worried about how long it would take, but my burning desire to know which two tram stops are the closest together was too strong, and I decided to just implement the naïve solution and wait out however long it would take. But here’s the thing—the code finished instantly. Two million operations – while an order of magnitude larger than 18,200 – is tiny for a computer.
The answer returned by my code returned another issue. The answer returned were two stops on the Chapel Street-Commercial Road intersection. This seemed strange to me, and after writing a quick script to visualise them I realised the issue. My code was matching stops on different tram routes. Obviously, tram stops on different routes are close together, that’s the whole point of an intersection.

The data I was using was a big list from Victorian Government, but it didn’t list the route associated with each stop. But it did list the stop number, ie. Melbourne University, Stop #1. My solution then was a slightly ugly one. I checked that every stop’s number was only one more or one less than the stop it was being compared to. This worked! Kind of.

I was still getting back edge cases of stops that happened to have the same number, but were on different routes. This was usually cases like Camberwell Junction, were the 70 and 75 split off, continuing to increase their stop numbers in different directions. My solution was to write some code to help me manually go through and verify these were accurate.

After that I got my answer: Caulfield Station to Derby Road. Astute readers might notice that this is different to the answer listed above. At the time, I was really unsatisfied with that answer, because I couldn’t with any confidence know that this was the closest stop. My code was just too ugly and unspecific. Looking closer at my data, I realised I was matching tram stops that were on different sides of the road as well.
I decided to scrap this and start again with different data. When I was making my game Suburble, I used data from Open Street Maps (OSM) for the tram line data. If you don’t know OSM, you should, they are great. It’s basically Wikipedia for Google Maps, with every detail and map feature being crowdsourced by the general public. Thanks to volunteers and neurodivergence, this data is very detailed and very accurate. They also have a scripting language that lets you extract their data into json. It’s a very weird language that I don’t really understand, but I’m usually able to stumble my way through it for simple queries like this.
[out:json];
relation["network"="PTV - Metropolitan Trams"]["type"="route"]["route"="tram"]({{bbox}})->.routes;
.routes out;
.routes >;
node._["railway"="tram_stop"];
out;
The data on OSM was much more detailed. It didn’t take me that long before I had my code churning through it, and it popped out an answer I was much more satisfied with.
If you’d like to play around with my code, it’s available on my Github. I did the data processing with Pandas, and the actual closest point problem with Rust. Why Rust and not Python? Because I felt like it, and I want to get better at Rust. That should probably give you some insight into the quality of the code. I also made a little TUI with Ratatui, to better help me visualise the data.
Thanks for reading! These kinds of multi-language problem solving projects are always very fun.
Subscribe to my newsletter!