The Great British Public Toilet Map is an open source, community driven project dedicated to helping people find toilets across the UK, with around 14,000 loos recorded and counting. In 2021 we took on the challenge of migrating the project from a SPA React app written in JavaScript to NextJS and Typescript. Together we'll discover why we decided it was time to migrate, the myriad technical challenges we faced along the way, how this work benefits our users, and the many exciting plans we have for the future.
Making a Splash: The Story of a Toilet Map Migration
AI Generated Video Summary
The Talk discusses the migration of the Toilet Map Project from Create React app to Next.js, with plans to take it international. The project aims to address the inadequate public toilet provision in the UK. It includes publicly accessible toilets, community access schemes, and user contributions. The migration to Next.js offers benefits like server-side rendering and root prefetching. The Talk also covers the migration to TypeScript, improvements in loading speed and caching, dynamic pages, API migration, geohashing, marker clustering, chunk loading, accessibility overlay, and future plans for expansion and user contributions.
1. Introduction to the Toilet Map Project
Hello, my name is Oliver, and I'm here today to tell you about a migration from Create React app to Next.js. The project in hand is the toilet map. It was previously known as the Great British Public Toilet Map. But we have plans to take it international. I'll cover why it's important that we're doing this work. Here's the logo and the URL. The state of public toilet provision in the UK is not great. We've lost over 10% of the public toilets in the UK in eight years. As a project, we're trying to sort the situation out.
Hello, my name is Oliver, and I'm here today to tell you about an interesting story that's taken place over the last year or so, probably a little bit more. And it involves a migration from Create React app to Next.js.
The project in hand is the toilet map. It was previously known as the Great British Public Toilet Map. But we have plans in the future to maybe take it international. So it's quite an exciting project. I've been involved with it for I think around two years total in varying degrees of involvement. But over the last year, like I said, this has been quite a concentrated project to get this project fully modernised.
So I'm going to tell you a story, essentially why we did it. We're going to look at some of the interesting highlights from that journey. And I'll also cover for a brief moment why it's important that we're doing this work. So without further ado, let's dive straight in.
So here's the logo and the URL, if you fancy visiting. And this is what it looked like in the first iteration. So this would have been around 2010, 2012, way before my first involvement in the project. And it was built, I think, using jQuery, pretty antiquated tech by today's standards, but it was already doing its job. And I think it only covered London boroughs. But very quickly, these things came along, and it was ordered to mobile. So we started to have more responsive layouts. And in its latest iteration, it's a bit like this. So we really put effort into making it responsive and easy for people to use, especially when they're on the go.
What is the state of public toilet provision in the United Kingdom right now? Unfortunately, it's not great. The person who founded the projects, Gayle Ramster, she's a senior research associate at the RCA, is quite critical of the state of public toilets in the UK, it's not a great situation. And from that quote, you can probably tell, for a fairly large portion of people, this is quite a big deal. Just before I was putting these slides together, I was browsing the web for some other quotes around this. This one particularly stood out to me, that's in eight years, we've lost over 10% of the public toilets in the UK, which is a pretty big deal. This is covered on various news outlets. And it really, well, while it is covered, there could probably be a lot more work put into helping this situation. And as a project, that's what we're really trying to do. We're trying to sort the situation out in as best way as we can, given that we don't actually help to maintain toilets in the UK.
2. Public Toilets and Data Integration
There are publicly accessible toilets provided by various businesses that we include in the map. We also have community access schemes where businesses can register. The data set consists of 14,000 active locations, with user contributions being the main source of expansion. During lockdown, we merged with Lucations UK and faced challenges in integrating their data. The migration to Next.js offered features like server-side rendering and root prefetching.
Yeah, this is what they look like. It's a pretty bad situation. Fortunately, there's another type of toilet that I haven't mentioned yet, known as publicly accessible toilets. These are provided by all kinds of businesses. And these are what we look to include in the map. So it's not just public toilets. It's toilets from a range of businesses and anyone can contribute.
On top of that, we also have community access schemes. So these businesses, they can sign up through schemes run by local councils to register so that the people who really need toilet can look on several data sets, but also on our map, where we've taken this data and plotted it. So you can easily find out where you can go locally.
So what does this data look like? We've got about 14,000 loopholes which have been marked as active and over time we've seen around 2,000 of these removed or updated. The open data for this in part is provided by the government and various other sources over the years. But user contributions, especially in the fairly recent past have been the main way that we've expanded our data set. People contribute by going to this page on the website and we have a nice little dialogue there where you can enter all the information around the toilet. We also have this explorer tool so you can check out the data set, see what's been updated recently, and go back to the very start. So you can see back in 2014 when this was first put together.
Briefly during lockdown, there was an amazing effort by an organisation called Lucations UK to do a pretty similar thing to what we do. So they got really active, gained a lot of traction on social media and loads of people contributed. It was an amazing effort and after about I think about a year of activity, they decided that they would merge with us. So we've been given their data and we've got a really interesting technical challenge on our hands to take their data and integrate it with what we have on the project. It's definitely not simple because some of these locations could contain duplicates, some of them aren't very accurate. It's an interesting set of problems.
So let's get on to the migration itself. What did it look like from a technical perspective? Well, as I mentioned, the project was a Create React app. So very client-side, JavaScript-heavy, no server-side rendering. And myself and Rupert, my collaborator on the project, took a look at what was available back in 2021 in terms of frameworks that we could move to and next stood out. The features really speak for themselves and as listed here, the ones that really stood out to us were around the server-side rendering and the ability to support root prefetching was especially interesting because we've got loads of markers on a map, it'd be great to be able to prefetch some of these markers with a framework like this. So, yeah, we started around here. So we thought, okay, trend's starting to go up, that's probably quite a good thing, let's give it a go. This is what we went from.
3. Migration to TypeScript and Next.js
We migrated to TypeScript, improving UX, loading speed, and caching pages. Developers will find it easier to contribute. Server-side rendering improves loading time. Migration considerations included links, images, and image optimization.
So we managed to migrate over to TypeScript during the course of the project, and we took full advantage of the fact that TypeScript is built with incremental adoption in mind. We also managed to achieve far better UX, we made it so that toilets loaded faster, we grouped the markers so it was easier to see them on the map, and we started caching the pages using some of the amazing features that Next offers.
Also, for developers, anyone wishing to contribute to the project now will find it a far, far easier experience, because we've adopted a language which is possibly one of the most popular on the planet these days. Also, the server-side rendering, so you don't see that anymore, you don't have JavaScript for some reason, you see that now. Things will load a little bit faster because you're not having to pull a massive JavaScript bundle and execute it, instead you're just getting the HTML for the page.
So, let's take a whistle-stop tour of some of the things that we had to consider when we were doing that migration. Links, mainly a copy-paste job, not too bad. Images, also pretty copy-pastable. We also got to take advantage of some of the features that Next offers around images, so they handle a lot of the stuff that we would have had to cope by hand, like responsive image sizing, and the one that really interested me was around place holders. Could we load in a blurred version of the image or other kinds of optimizations, because, at the end of the day, what's driving our work here is to help people find a loop.
4. Dynamic Pages and API Migration
Dynamic pages, moving the API over, GraphQL architecture, Redis cache layer, Aloo, running the GraphQL server, authentication, improving user experience, optimizing filters, representation using bits.
Dynamic pages, a pretty interesting feature. Here's just a very small snippet of how we generate one of these pages. We get a connection to the database, and we render the page. By the end of that method, you should have a fully rendered Lou detail panel for your mobile device.
In terms of moving the API over, it was originally offered using the express framework. Luckily, Next makes it super simple to take something like that over across to their platform. In terms of GraphQL, which we use as a front on top of our Mongo database, most of it was left largely unchanged, so this is what the architecture looks like now. Where the GraphQL plus the Next.js parts lives would have been the express server, but now Next has kind of subsumed that Create React app and we've managed to put it all on the same level stack. We've also added this nice little addition of a Redis cache layer, which means that when a user requests a map tile, we first check in our cache, and if it's there, we will give them that instead. This is what Aloo looks like. It's kind of evolved over many years, so the shape of it isn't perfect, but it does the job, and very quickly, this is how you grab Aloo from our GraphQL resolver, and in terms of running the GraphQL server, we connect to our database, we start Apollo server, and you have your API root.
In terms of authentication, we were originally using Auth0, and we still are. In fact, Next makes it a dream to handle. They provide a high order component, and we could wrap one of our routes with that, and everything worked as expected. So, another key part of this work was improving the overall user experience of the project. So, that was all around actually the mechanics of moving from create react app to Next. These are some things we did as on top of that to really just make the site better for everyone. The first thing we did was we took the size of an object for an individual loop down from a pretty big blob of JSON, around 260 bytes into a single string. And we're very quickly gonna look at the anatomy of that string and how we're able to represent pretty much exactly the same information in a small amount of space.
So, if we break that string down, you'll see that it's make up of the lose ID which is unique. A geohash and a filter. Before I started this, I had no idea what a geohash was, and we'll dive into that briefly. But first of all, we're gonna have a look at how we optimize these filters. So, as a user of the map, you have these switches, and they allow you to filter by various criteria. It's a kind of useful feature. These are easily represented using just bits. Part of a binary number I'm sure you're familiar with. And that was quite a good realization, because it meant that all of a sudden, we could represent the state of these switches using those bits. And that's important, because we went from a fairly large piece of information to just a single number to represent any combination of those filters we wanted to. So, in this demonstration, we've got the very first bit highlighted, which means it's a no payment toilet.
5. Improving Filters with Bit Operations
We run an OR operation on the options and use a mask to determine the status of each bit. This improved the filters.
And the fifth one is highlighted, meaning that it's also got baby-changing available. So, we run an OR operation on both of those two options. And the result of that operation is the number 17, or a binary word with those two bits set. And then once that's been sent to the client side, all we have to do is take the mask and do an AND operation against the first and the fifth bits to determine whether they're on or off. So, as you can see, we've run through each of the bits and we've determined that it's free and it has baby-changing. So, that's how we made the filters a bit better.
6. Geohash and Marker Clustering
Let's look at what a geohash is. It's a way of representing a location using a string of characters. By changing the length of the hash, we can make it more precise. This enables us to turn latitude and longitude into a consistently sized string. We employed a library called MarketBuster to cluster our markers and improve loading speed.
Let's look at what a geohash is. You know, I found these really exciting, but first we're going to talk about what3words very quickly. It's a product you might have heard of and it's a way of representing a location using, you guessed it, just three words. This is kind of similar in a way to that, but different in a very important sense.
And how is it different? So this is a pretty concise description of what it does. I think from the creator, I can't quite remember. But the key thing here is that by changing the length of our hash, we can make it more precise. So if it's only one character, it will represent a pretty wide chunk of the world. But as you increase precision on those characters, that very clever algorithm, which I couldn't start to understand, pieces together those characters into an increasingly precise representation of a location on Earth. So this is what that string represents in London. A little chunk of land, right next to Trafalgar Square.
So that was a pretty amazing realization for us. It meant that we could take a pretty long latitude and longitude and turn them into a consistently sized string of pretty good precision. It's enough for being able to find the toilet that's listed. And of course with any compression scheme you could always take it even further. So the ID is still quite long. We could probably turn this into a sequence of numbers. So we just sequentially refer to each loop or some other kind of mapping operation. But life is short and we did a pretty good job. In terms of the fixed length of it, we could have just got rid of those pipes because we know the length of this string. We don't really need to separate it. That was really just for development.
So that was all pretty exciting. I really enjoyed working on that. That was, if anything, just satisfying being able to get the amount of data down. And on the technical side, pretty critical, because it meant that all of a sudden we're sending far, far less information to the client. And what this enables us to do is go from something that looked like this, where we were loading in everything in a pretty jumbled mess, to a system of clustering. So instead of that, we employed a library called MarketBuster, aptly named. And it lets us chuck our existing markers into a group, render the group, and magically, they suddenly look like this, which is far better if you want to get a picture of a wider area, so if you're going holiday somewhere. The problem with that, even though it's great, is that clustering 13,000 markers is slow when you first load the page.
7. Chunk Loading and Accessibility Overlay
We implemented a system of chunk loading to prevent the map from freezing on older devices. By requesting toilets within specific geohashes, we can load markers for each box and render them on the client side. With increased level of detail as you zoom in, the map now provides a better user experience. An accessibility overlay allows users with visual impairments to access toilet data by providing a list of toilets within the visible area. We also added a query to batch load location names on demand.
It's a blocking operation. It could freeze the map on an older device for about five seconds, which isn't great at all. So we thought about a system of chunk loading. It's pretty simple in concept. We look at all the geohashes within the viewport. These are some lower resolution geohashes, and within those boxes, we can request all of the toilets. So here we have the operation, and here we have the code to actually get that information on the server side. I'm not going to step through it now, but if you're interested, it's all on GitHub.
And as a result of that operation, you get a list of all the loads in the box. And then on the client side, we have some other React code, which is used to load in the markers for each specific box, and to visualise it, it looks something like that. So each of those boxes is another instance of that component. And this is one of those auto generated hooks, which I mentioned, which is just generated from our GraphQL schema, and we can call it Apollo will cache the result. Things are quite fast now. And in terms of during the markers, we have two hooks, which allow us to stop rendering markers when they leave the viewport, so we can only render what's on the screen.
So, now when you're using the map, it looks a bit like this. So, behind the scenes, we increase the level of detail as you zoom in, using a method like this. And as a demonstration, this is what it looks like for a user these days. Far better to use. Much nicer experience and hopefully helping a lot of people in need find a toilet where no one will go. So, another exciting thing was an accessibility overlay which provided the ability for people who maybe couldn't see as well when they're using application to use a dialogue that appears over the map. It's just overlaid on the top. And it provides a list of the first five toilets within that box that you see in the middle. So, as you pan around the map, even without being able to see, you can still access the data that we provide. Pretty important for accessibility. So, we don't really add the name to the compressed string. So, we just had to add another query which allows us to batch the loon names on demand. It will hover over a bot. You move the bot and find new names. This is what it looks like. So, you can tap around with your RPs.
8. Future Plans and Call for Contributions
We implemented comprehensive Cypress tests for continuous integration. We actively gather user feedback and plan to expand the project to other countries. We have questions about internationalization, data storage, and infrastructure funding. The project is a group effort and welcomes contributions from various perspectives. Thank you for listening and supporting our mission.
And access all the same data that anyone else could. In terms of testing and GitHub actions, this was pretty new to the project. And we moved, essentially, from no continuous integration, apart from some very, very broken end to end tests, to a very comprehensive suite of Cypress tests, which has been pretty incredible for us as contributors, because it means we can move faster and with more confidence, especially as it's just the two of us.
So very quickly, I want to tell you all about the future. What else do we have in store for this project? There's quite a lot, if I'm being completely honest. The thing about a product like this is everyone needs to use the toilet, everyone has an opinion on what could be changed. Everyone wants, can immediately relate to the kind of problems that we're looking to solve. I've never had a conversation with somebody who has gone and not had a suggestion for a new feature. And we keep track of these on GitHub. So we make issues whenever something comes through.
We also actively gather user feedback. So we have a little user feedback box on the website. And all of the time, people will just put their thoughts as they're using the app and give us an idea of what could be improved. And often, it's stuff which we would have never have spotted ourselves. On top of that, it's only limited to the UK right now. There are some toilets mapped in other countries. But it's definitely not officially supported by the project. So a massively interesting piece of work that we have coming up. And we'd love for help with is around the question of how do we take this to other countries? Do we need to think about internationalization problems? How do we store the data? Right now, we run on a very limited budget of around 40 pounds a month. How do we allow someone who wants to start their own version of the map in France to pay for that infrastructure? Would it be syndicated perhaps? These are all pretty cool questions which I could spend another hour talking about.
But I'm going to draw the talk to a close with this little 3D printed toilet map logo I made for Rupert for Christmas. I thought it would be a lovely little present for him. And a very quick shout out to everyone else who's contributed to the projects over the years. It's been a real group effort. And it's definitely still ongoing. And with all this work that we've been putting into it, we really hope that we'll get more contributions from people who are interested whether from the tech perspective, user experience side of things, or just from an advocacy point of view if you're interested in helping people find a toilet, this project is for you. Thank you so much for listening. I hope you learned something. And if anything, I hope I'm very glad that I could spread awareness of the project because I believe it's doing real social good.
Comments