Divide and conquer.
180 x 360 areas of the globe based on lat/lon degrees.
Each with a list of all it's airports.
Registration is done once. Even at 100x4188 airports, it is <2 seconds
Searches will always take only 16msecs.
Edit: I have replaced the distance computation with the Haversine algorithm to make it more accurate.
Revised code NearX_V4 is attached below.
Edit: Disclaimer: Use at your own risk. Especially if you're flying a plane!
Log of results:
Nearby Airports
YOUNGSTOWN YNG YOUNGSTOWN WARREN RGNL 364km
WILLIAMSPORT IPT WILLIAMSPORT RGNL 319km
BUFFALO BUF BUFFALO NIAGARA INTERNATIONAL 123km
WATERLOO YKF WATERLOO RGNL 187km
HAMILTON YHM HAMILTON 168km
TORONTO YKZ BUTTONVILLE MUNI 98km
TORONTO YTZ CITY CENTRE 106km
TORONTO YYZ LESTER B PEARSON INTERNATIONAL 122km
TORONTO YZD DOWNSVIEW 108km
NIAGARA FALLS IAG NIAGARA FALLS INTERNATIONAL 114km
ROCHESTER ROC GREATER ROCHESTER INTERNATIONAL 102km
WATERTOWN ART WATERTOWN INTERNATIONAL 172km
SYRACUSE SYR SYRACUSE HANCOCK INTERNATIONAL 191km
MUSKOKA YQA MUSKOKA 145km
PETERBOROUGH YPQ PETERBOROUGH 34km
TRENTON YTR TRENTON 54km
KINGSTON YGK KINGSTON 129km
PETAWAWA YWA PETAWAWA 232km
Closest: PETERBOROUGH YPQ PETERBOROUGH 34km
Search time: 16milliseconds
Sub Class_Globals
Type Point(X As Float, Y As Float) 'Move to Sub Class_Global
Private Root As B4XView 'ignore
Private xui As XUI
Private Areas(180, 360) As List
End Sub
Public Sub Initialize
End Sub
Private Sub B4XPage_Created (Root1 As B4XView)
Root = Root1
Dim airports As List = File.ReadList(File.DirAssets, "GlobalAirportDatabase.txt")
'AYGA:GKA:GOROKA:GOROKA:PAPUA NEW GUINEA:006:004:054:S:145:023:030:E:01610:-6.082:145.392
Dim airportCount As Int
Dim markTime As Long = DateTime.now
For Each s As String In airports
Dim v() As String = Regex.Split("\:", s)
If v(8) <> "U" Then
Dim pt As Point = LLToDecimal(Array(v(5), v(6), v(7), v(8)), Array(v(9), v(10), v(11), v(12)))
Dim ptx As Point = CreatePoint(pt.X + 90, pt.Y + 180)
If Areas(ptx.X, ptx.Y).isInitialized = False Then Areas(ptx.X, ptx.Y).Initialize
Areas(ptx.X, ptx.Y).Add(Array(v(3), v))
airportCount = airportCount + 1
End If
Next
Log("Registration time: " & (DateTime.Now - markTime) & "milliseconds" & TAB & "For " & airportCount & " Airports")
Log(TAB)
Dim cities As Map = CreateMap( _
"Cobourg": CreatePoint(43.95977, -78.16515), _
"Melbourne": CreatePoint(-37.814, 144.96332), _
"Christchurch": CreatePoint(-43.5320, 172.6306), _
"Greenwich": CreatePoint(51.4934, 0.0098))
Log("Nearby Airports")
Dim example As String = "Cobourg"
markTime = DateTime.now
Dim found As List = findNearest(cities.Get(example), 2)
Dim closest() As Object
Dim minDist As Float = 1 / 0
If found.Size > 0 Then
Log(found.Size)
For Each ar() As Object In found
Dim v() As String = ar(1)
Dim thisExact As Point = LLToDecimal(Array(v(5), v(6), v(7), v(8)), Array(v(9), v(10), v(11), v(12)))
Dim dist As Float = DistanceKM(cities.Get(example), thisExact)
If dist < minDist Then
minDist = dist
closest = ar
End If
Log(ar(0) & TAB & v(1) & TAB & v(2) & TAB & Ceil(dist) & "km")
Next
End If
ar = closest
v = ar(1)
Log(TAB)
Log("Closest: " & ar(0) & TAB & v(1) & TAB & v(2) & TAB & Ceil(minDist) & "km")
Log(TAB)
Log("Search time: " & (DateTime.Now - markTime) & "milliseconds")
End Sub
Private Sub findNearest(pt As Point, delta As Int) As List
Dim result As List = CreateEmptyList
Dim ptx As Point = CreatePoint(pt.X + 90, pt.Y + 180)
For i = Max(0, ptx.x - delta) To Min(179, ptx.x + delta)
Dim xi As Int = i
For j = ptx.y - delta To ptx.y + delta
Dim xj As Int = j
If xj < 0 Then xj = xj + 360
If xj > 359 Then xj = xj - 360
If Areas(xi, xj).IsInitialized Then
For Each ar() As Object In Areas(xi, xj)
result.Add(ar)
Next
End If
Next
Next
Return result
End Sub
Note see attached zip for complete code, including some helper subs.