As a micromobility provider, Beam engages with the governments of our operating cities to ensure riders are riding safely, and the laws of the individual cities are abided to. How that looks like varies in every city.
For example, Adelaide has a pedestrian street mall that the government did not want people riding into. To solve for that, we created a no riding zone that would automatically slow to a stop any Beam vehicles that entered into that area.
Beam has many different types of geofences to cater to different use cases, such as general operating zones, speed-limited zones, and no parking zones. Many of the cities we operate in have a good mixture of all of them.
Imagine being in a city with just an operating and non-operating zone.
Before launch, we realize we want deeper insights into how some areas of the city perform. So we break the operating geofence into multiple operating geofences.
After a couple of weeks, we realize that vehicles parked near a river have a higher probability of being thrown into the aforementioned river. With this knowledge, we make all areas near rivers no parking zones.
Sometime later, we find places in the city where vehicles always get stolen. Certain coffee shops also don’t like our vehicles parked near them. We add no parking zones to those places as well.
Over time, we also spot a few streets that always have high pedestrian footfall. To minimize the risk of accidents, we put slow zones in those areas.
At the start, our system did not allow geofences to overlap with one another. Geofences are manually drawn, and care had to be taken to ensure they do not overlap. This meant a significant amount of effort was spent every time we added or changed a zone.
Each geofence had its own individual settings too. If we wanted to change the speed limit of slow zones, it was a matter of changing the speed for each slow zone, rather than changing it once for all slow zones.
This was not scalable with launching even more cities and we were not setting ourselves up for success.
The most obvious solution was to change what our system could take. Overlapping geofences and multi-polygons was the missing piece that would make our lives a lot easier.
Geofences now have a priority property and can be stacked in layers. The lowest priority geofence will be at the bottom and higher priority geofences on top. When viewing top-down, what you see is the actual geofence that gets applied.
Usually, geofences with operating zones would have the lowest priority. Adding a slow zone in an area that’s a current operating zone is as simple as creating a new slow zone geofence with a higher priority than the operating zone. The system will do the relevant calculations.
This solution did bring about its own set of challenges
We found out that cities were not putting as many slow or no parking zones as they wanted to because of how tedious and error-prone it was. With this system in place, they proceeded to add in the zones they wanted to, resulting in a complex polygon that Google Maps refused to display. This was a problem since our app used Google Maps to display to our riders the different geofences and scooters.
We spent some time researching on why that could happen, including changing the directions of the polygon points (clockwise and anti-clockwise points matter for holes in polygons), but was unable to find a solution.
Thankfully, we were also working on moving to Mapbox at that time, and a quick test showed us that Mapbox was able to render the complex polygon without problems.
With the rendering problem solved, we thought that we were finished with this project. However, within a week our error alerting system started throwing up errors related to geofences.
Looking into the logs, we saw some errors related to Turf.js. For some of them, Turf complained that the object we passed to Turf was not a polygon. What was perplexing was that Turf defines a polygon as an area with at least 3 points, and our system did not allow areas with less than 3 points to be submitted.
The first solution was to modify the turf library, such that whenever there was a line (2 points) or a dot(1 point), add additional duplicate points so that it was still a polygon. This worked for a while until some other problems appeared later on.
We decided to experiment with PostGIS instead as we were already using them for other purposes, and it turned out that it worked! As it’s an expensive database operation, we took special care to make sure that it only ran when related polygons changed.
As a fast-growing startup, Beam has many exciting scalability problems such as this. We hope that this post gives some insight into the operations that go on behind every Beam ride.
What works on a small scale may not work on a much larger scale and can be prone to human error (It happened before!). As engineers working on the product, we want to set people up for success as much as possible, and reducing chances for human error is one way to do so.
If this work sounds exciting to you, or you have a passion for micromobility, Beam is hiring for Fullstack Software Engineers and other positions. Please contact David Ten for more info.