11import Foundation
22import Vapor
33
4-
54extension GeocodingDatabase {
65 static let geonamesFile = URL ( fileURLWithPath: " data/allCountries.txt " )
76 static let alternateNamesFiles = URL ( fileURLWithPath: " data/alternateNames.txt " )
87 static let databaseFile = URL ( fileURLWithPath: " data/database.bin " )
9-
8+
109 /// Read geonames txt files and create an index protobuf file
1110 public static func createDatabase( logger: Logger ) throws {
1211 logger. info ( " Create new geocoding database " )
1312 let alternativeNamesData = try Data ( contentsOf: alternateNamesFiles, options: [ . mappedIfSafe, . uncached] )
1413 let alternate = AlternateNames ( data: alternativeNamesData, logger: logger)
15-
14+
1615 let geonamesData = try Data ( contentsOf: geonamesFile, options: [ . mappedIfSafe, . uncached] )
1716 let geonames = GeocodingDatabase . Geonames ( data: geonamesData, alternativeNames: alternate, logger: logger)
18-
17+
1918 let searchTree = GeocodingDatabase ( geonames: geonames, logger: logger)
2019 let data : Data = try searchTree. serializedData ( )
2120 let start = Date ( )
2221 logger. info ( " Write database to disk " )
2322 try data. write ( to: databaseFile)
24- logger. info ( " Database written in \( Date ( ) . timeIntervalSince ( start) ) seconds, size \( ByteCountFormatter ( ) . string ( fromByteCount: Int64 ( data. count) ) ) " )
23+ logger. info (
24+ " Database written in \( Date ( ) . timeIntervalSince ( start) ) seconds, size \( ByteCountFormatter ( ) . string ( fromByteCount: Int64 ( data. count) ) ) "
25+ )
2526 }
26-
27+
2728 public static func loadOrCreate( logger: Logger ) throws -> GeocodingDatabase {
2829 if !FileManager. default. fileExists ( atPath: databaseFile. path) {
2930 try Self . createDatabase ( logger: logger)
@@ -32,45 +33,63 @@ extension GeocodingDatabase {
3233 logger. info ( " Loading existing database... " )
3334 let data = try Data ( contentsOf: URL ( fileURLWithPath: " data/database.bin " ) , options: [ . mappedIfSafe, . uncached] )
3435 let searchTree = try GeocodingDatabase ( serializedBytes: data)
35- logger. info ( " Finished loading in \( Date ( ) . timeIntervalSince ( start) ) seconds, \( searchTree. geonames. geonames. count) entries " )
36+ logger. info (
37+ " Finished loading in \( Date ( ) . timeIntervalSince ( start) ) seconds, \( searchTree. geonames. geonames. count) entries "
38+ )
3639 return searchTree
3740 }
38-
41+
3942 /// Search for a string in the indexed location names and one language index
4043 public func search( _ searchString: String , languageId: Int32 , maxCount: Int ) -> [ ( Int32 , Float ) ] {
4144 let stripped = searchString. folding ( options: . diacriticInsensitive, locale: nil ) . lowercased ( )
4245 let results = PriorityQueue ( length: maxCount)
4346 let onlyExact = searchString. count <= 2
4447 index. search ( Substring ( stripped) , results: results, onlyExact: onlyExact, geonames: geonames. geonames)
45- languageIndex [ Int ( languageId) ] . search ( Substring ( stripped) , results: results, onlyExact: onlyExact, geonames: geonames. geonames)
46- return results. queue. filter ( { $0. id > 0 } )
48+ languageIndex [ Int ( languageId) ] . search (
49+ Substring ( stripped) ,
50+ results: results,
51+ onlyExact: onlyExact,
52+ geonames: geonames. geonames
53+ )
54+ return results. queue. filter ( { $0. id > 0 } )
4755 }
48-
56+
4957 /// Search for a string in the indexed location names and one language index
50- public func proximity( latitude: Float , longitude: Float , maxCount: Int , maxDistanceKilometer: Float ) -> [ ( Int32 , Float ) ] {
51- let results = geotree. knn ( latitude: latitude, longitude: longitude, count: maxCount, maxDistanceKilometer: maxDistanceKilometer, elements: geonames. geonames)
52- return results. filter ( { $0. id > 0 } )
58+ public func proximity(
59+ latitude: Float ,
60+ longitude: Float ,
61+ maxCount: Int ,
62+ maxDistanceKilometer: Float
63+ ) -> [ ( Int32 , Float ) ] {
64+ let results = geotree. knn (
65+ latitude: latitude,
66+ longitude: longitude,
67+ count: maxCount,
68+ maxDistanceKilometer: maxDistanceKilometer,
69+ elements: geonames. geonames
70+ )
71+ return results. filter ( { $0. id > 0 } )
5372 }
5473
5574 public init ( geonames: Geonames , logger: Logger ) {
5675 logger. info ( " GeoTree: Start loading " )
5776 let startTree = Date ( )
5877 self . geotree = GeoTree ( elements: geonames. geonames, depth: nil )
5978 logger. info ( " GeoTree: Finished loading in \( Date ( ) . timeIntervalSince ( startTree) ) seconds " )
60-
79+
6180 let start = Date ( )
6281 logger. info ( " SearchTree: Start loading " )
63-
82+
6483 self . geonames = geonames
6584 var languageIndex = [ SearchTreeLoader] ( )
6685 languageIndex. reserveCapacity ( geonames. languages. count)
6786 for _ in geonames. languages {
6887 languageIndex. append ( SearchTreeLoader ( ) )
6988 }
70- let languageEmpty = geonames. languages. firstIndex ( where: { $0 == " " } ) !
71- let languageIata = geonames. languages. firstIndex ( where: { $0 == " icao " } ) !
72- let languageIcao = geonames. languages. firstIndex ( where: { $0 == " iata " } ) !
73-
89+ let languageEmpty = geonames. languages. firstIndex ( where: { $0 == " " } ) !
90+ let languageIata = geonames. languages. firstIndex ( where: { $0 == " icao " } ) !
91+ let languageIcao = geonames. languages. firstIndex ( where: { $0 == " iata " } ) !
92+
7493 logger. info ( " SearchTree: Prepare language trees " )
7594 for (id, geoname) in geonames. geonames {
7695 if !geonames. includeInSearchIndex ( featureCode: geoname. featureCode) {
@@ -83,8 +102,8 @@ extension GeocodingDatabase {
83102 languageIndex [ Int ( name. 0 ) ] . add ( name. 1 , id: id)
84103 }
85104 }
86- self . languageIndex = languageIndex. map ( { $0. immutable ( ) } )
87-
105+ self . languageIndex = languageIndex. map ( { $0. immutable ( ) } )
106+
88107 logger. info ( " SearchTree: Load main index " )
89108 let index = SearchTreeLoader ( )
90109 for (id, geoname) in geonames. geonames {
@@ -102,7 +121,7 @@ extension GeocodingDatabase {
102121 }
103122 }
104123 self . index = index. immutable ( )
105-
124+
106125 logger. info ( " SearchTree: Finished loading in \( Date ( ) . timeIntervalSince ( start) ) seconds " )
107126 }
108127}
0 commit comments