Your core algorithm is about as good as you can do. However you can reduce the number of items you have to examine, by first sorting them into buckets based on location. For example, you could have a bucket for each 1km square of map.
If you know that every point on the map has a bus stop within 1km, then you only need to search through the stops in the target 1km square, and its 8 neighbours -- since a 1km radius circle drawn around any point on the centre square will be wholly contained in those 9 squares.
In many real-world circumstances, based on how you know the stops are spread you can probably come up with a cheap way to select a small set of squares guaranteed to contain the stop you're looking for. It could be very simple ("Nowhere on the map is further than 1km from a stop, so the 3x3 squares around the coordinates will contain one") or more technical ("in the centre of the city, a 3x3 search guarantees a hit; in the outskirts I need 5x5")
If the algorithm needs to be more general, then you can start by searching those 9 squares, then add squares overlapping an ever-larger circle.
radius = 1
while(true) {
Set<Square> squares = difference(
squaresContainedIn(radius),
squaresContainedIn(radius - 1));
busStop = findNearest(coords, squares);
if(busStop != null) {
return busStop;
}
radius ++;
}
Beware though; you'll have to refine this algorithm a bit. It's possible for the closest item to be two squares away, even if there's a candidate one square away:
a 33333
b 32223
c 32123
d 32223
e 33333
ABCDE
The closest stop by cartesian distance could be in E,c
(second "search band") even though there's an item in the far corner of B,d
(first "search band"). There are various ways you can make the algorithm handle this:
- When examining a band, disregard stops not in the current radius range. You will need to look at these squares again when considering the next iteration of
radius
.
- Or, when you find a candidate, don't consider it the "final" item until you've also searched the next band.
Or, depending on real-world requirements, you might decide that the occasional wrong (but still useful) answer is an acceptable trade-off for speed.
If you want to scale this approach up, you could use a QuadTree structure in which squares are nested within larger squares.