“Find Bands in my Area” is what I want the Local page in Indie Composers to do. Open the site, find someone nearby, put some music on.
You can open the page without signing in or telling me where you live. I've got a list of bands but I don't know which ones are near you.
There are two boxes labelled Longitude and Latitude. Knowing those offhand is quite an ask when you've come here for music. Most of the rest of the page is different ways of filling them.
Roughly here
First I need somewhere to put the map without making you fill in a form or work out those two numbers before you can see any bands. A GeoIP service can give me a country, region, city and coordinates from your IP address. I save that answer in MySQL so I'm not asking again straight away for the same address. It's labelled as an estimate because the location attached to an internet provider isn't necessarily where you're sitting. I want something to put on the map when the page opens, but you can change the coordinates if the estimate is wrong.
The map starts there, and the page gets a flag as well. It tries the country flag, then the region, then the city. If it can't find any of them, you get Antarctica.
Which, I suspect, is a difficult place to build a local music scene.
You can give it a postcode instead. There's a dropdown with UK, USA, Jersey and Canada, then Google's geocoder turns the postcode into coordinates. Yes, Jersey gets its own entry in a list of four. I live here.
Or you can type an address. I've ended up with postcodes, addresses, map clicks. At least they all fill the same two boxes so I only have to write the band search once.
If you can find a place on a map, asking you to go and look up its postcode seems a bit pointless. I can let you move the map to somewhere you recognise and click on it.
An estimate, and a bug
The browser can ask for a location too, it asks for permission, then gives the page a position or an error, and the map and typed options still work if that request fails. Someone can say no, or the request can time out. My desktop and mobile handlers don't quite agree here. The mobile one fills the input values with the returned coordinates, but the desktop one writes them into the inputs' HTML content instead, so it doesn't fill the boxes properly. I wouldn't rank all these options from least to most accurate. If the device gives you a poor estimate, clicking somewhere you know could be better. A long string of decimal places doesn't help much if it starts from the wrong place.
Where does the band go?
The band needs a location as well. If you're signed in you can save a point to your own account or to your band. Saving it to your account helps fill the form next time. Saving it to the band lets other people find you.
That second one is why the confirmation suggests using “a local pub or shop”. If I'm looking for a band near St Ouen, I need to know where the band is based, well, somewhere around St Ouen. I don't need the drummer's front door.
The coordinates are encrypted in storage but the site still shows people how far away the band is, and those distances can give the location away whatever the encryption is doing. I'd rather the band picked a sensible public place to begin with.
There's also an annoying detail in the code. The stored pair is longitude followed by latitude. The distance function wants latitude first. Same two numbers, opposite order.
For the example here I'm giving them names:
var visitor = {lat: 49.1858, lng: -2.1065};
var band = {lat: 49.1990, lng: -2.0250};
var miles = distanceInMiles(visitor, band);
I can read lat and lng. Two bare numbers would leave me checking which one I put first, again.
Now check all the bands
PHP reads through the bands now. It uses the haversine formula to find the shortest distance over a sphere. Here that's the Earth's surface, so it measures the crow's route rather than your drive. The encryption makes this a bit more work. MySQL can't put those encrypted strings in order of distance so PHP decrypts the coordinates, calculates each band's distance, and checks even the ones which will end up miles down the list.
On every search.
A band on Guernsey can look quite close. Getting across the sea is still your problem.
As each result comes in, I insert it before the first band that's further away. By the end of the loop the list is already in order. The distance gets rounded up and shown with “~ miles” since I'm only trying to give people a rough idea.
The earlier version asked Google to turn each band's postcode into coordinates during the search. Which is quite a lot of asking considering the band probably hasn't moved since the last visitor. Now I do that when the band saves its location and keep the coordinates.
There's a catch with giving out distances. Someone can move their search point around and work backwards from the answers to narrow down where the band is. Rounding the miles doesn't stop that.
I could pick a random point inside a circle around the band's saved location and use that for the distances and search order. I'd base the offset on the band's database ID and a secret value kept on the server, the pepper. That gives each band a consistent offset that someone couldn't reproduce from its ID alone.
Keeping it consistent matters. If I moved the point randomly on every search, I'd be giving someone more samples to work out the real location from.
I haven't added this. I'm still wondering how big to make the circle. Too small and it doesn't hide much. Too big and my nearby band starts looking as though it's somewhere else. It would still reveal the general area so I'd keep the advice about choosing a pub or shop.