29
29
30
30
'use strict' ;
31
31
32
+ var DEBUG = true ;
33
+
32
34
var fs = require ( 'fs' ) ;
33
35
var kdTree = require ( 'kdt' ) ;
34
36
var request = require ( 'request' ) ;
@@ -38,7 +40,9 @@ var async = require('async');
38
40
39
41
// All data from http://download.geonames.org/export/dump/
40
42
var GEONAMES_URL = 'http://download.geonames.org/export/dump/' ;
41
- var GEONAMES_FILE = 'cities1000' ;
43
+ var CITIES_FILE = 'cities1000' ;
44
+ var ADMIN_1_CODES_FILE = 'admin1CodesASCII' ;
45
+ var ADMIN_2_CODES_FILE = 'admin2Codes' ;
42
46
var GEONAMES_DUMP = './geonames_dump' ;
43
47
/* jshint maxlen: false */
44
48
var GEONAMES_COLUMNS = [
@@ -64,51 +68,146 @@ var GEONAMES_COLUMNS = [
64
68
] ;
65
69
/* jshint maxlen: 80 */
66
70
67
- var DEBUG = false ;
68
-
69
71
var geocoder = {
70
72
_kdTree : null ,
73
+ _admin1Codes : null ,
74
+ _admin2Codes : null ,
75
+
76
+ _getGeoNamesData : function ( ) {
77
+
78
+ } ,
79
+
80
+ _getGeoNamesAdmin1CodesData : function ( callback ) {
81
+ var now = ( new Date ( ) ) . toISOString ( ) . substr ( 0 , 10 ) ;
82
+ if ( fs . existsSync ( GEONAMES_DUMP + '/admin1_codes/' + ADMIN_1_CODES_FILE +
83
+ '_' + now + '.csv' ) ) {
84
+ DEBUG && console . log ( 'Using cached GeoNames admin 1 codes data from ' +
85
+ GEONAMES_URL + ADMIN_1_CODES_FILE + '.txt' ) ;
86
+ return callback ( null , GEONAMES_DUMP + '/admin1_codes/' +
87
+ ADMIN_1_CODES_FILE + '_' + now + '.csv' ) ;
88
+ }
89
+ DEBUG && console . log ( 'Getting GeoNames admin 1 codes data from ' +
90
+ GEONAMES_URL + ADMIN_1_CODES_FILE + '.txt (this may take a while)' ) ;
91
+ var url = GEONAMES_URL + ADMIN_1_CODES_FILE + '.txt' ;
92
+ request . get ( url , function ( err , response , body ) {
93
+ if ( err || response . statusCode !== 200 ) {
94
+ return callback ( 'Error downloading GeoNames admin 1 codes data' +
95
+ ( err ? ': ' + err : '' ) ) ;
96
+ }
97
+ // Store a dump locally
98
+ if ( ! fs . existsSync ( GEONAMES_DUMP + '/admin1_codes' ) ) {
99
+ fs . mkdirSync ( GEONAMES_DUMP + '/admin1_codes' ) ;
100
+ }
101
+ var fileName = GEONAMES_DUMP + '/admin1_codes/' + ADMIN_1_CODES_FILE +
102
+ '_' + now + '.csv' ;
103
+ try {
104
+ fs . writeFileSync ( fileName , body ) ;
105
+ // Housekeeping, remove old files
106
+ var currentFileName = ADMIN_1_CODES_FILE + '_' + now + '.csv' ;
107
+ fs . readdirSync ( GEONAMES_DUMP + '/admin1_codes' ) . forEach ( function ( file ) {
108
+ if ( file !== currentFileName ) {
109
+ fs . unlink ( GEONAMES_DUMP + '/admin1_codes/' + file ) ;
110
+ }
111
+ } ) ;
112
+ } catch ( e ) {
113
+ throw ( e ) ;
114
+ }
115
+ return callback ( null , fileName ) ;
116
+ } ) ;
117
+ } ,
118
+
119
+ _parseGeoNamesAdmin1CodesCsv : function ( pathToCsv , callback ) {
120
+ return callback ( ) ;
121
+ } ,
122
+
123
+ _getGeoNamesAdmin2CodesData : function ( callback ) {
124
+ var now = ( new Date ( ) ) . toISOString ( ) . substr ( 0 , 10 ) ;
125
+ if ( fs . existsSync ( GEONAMES_DUMP + '/admin2_codes/' + ADMIN_2_CODES_FILE +
126
+ '_' + now + '.csv' ) ) {
127
+ DEBUG && console . log ( 'Using cached GeoNames admin 2 codes data from ' +
128
+ GEONAMES_URL + ADMIN_2_CODES_FILE + '.txt' ) ;
129
+ return callback ( null , GEONAMES_DUMP + '/admin2_codes/' +
130
+ ADMIN_2_CODES_FILE + '_' + now + '.csv' ) ;
131
+ }
132
+ DEBUG && console . log ( 'Getting GeoNames admin 2 codes data from ' +
133
+ GEONAMES_URL + ADMIN_2_CODES_FILE + '.txt (this may take a while)' ) ;
134
+ var url = GEONAMES_URL + ADMIN_2_CODES_FILE + '.txt' ;
135
+ request . get ( url , function ( err , response , body ) {
136
+ if ( err || response . statusCode !== 200 ) {
137
+ return callback ( 'Error downloading GeoNames admin 2 codes data' +
138
+ ( err ? ': ' + err : '' ) ) ;
139
+ }
140
+ // Store a dump locally
141
+ if ( ! fs . existsSync ( GEONAMES_DUMP + '/admin2_codes' ) ) {
142
+ fs . mkdirSync ( GEONAMES_DUMP + '/admin2_codes' ) ;
143
+ }
144
+ var fileName = GEONAMES_DUMP + '/admin2_codes/' + ADMIN_2_CODES_FILE +
145
+ '_' + now + '.csv' ;
146
+ try {
147
+ fs . writeFileSync ( fileName , body ) ;
148
+ // Housekeeping, remove old files
149
+ var currentFileName = ADMIN_2_CODES_FILE + '_' + now + '.csv' ;
150
+ fs . readdirSync ( GEONAMES_DUMP + '/admin2_codes' ) . forEach ( function ( file ) {
151
+ if ( file !== currentFileName ) {
152
+ fs . unlink ( GEONAMES_DUMP + '/admin2_codes/' + file ) ;
153
+ }
154
+ } ) ;
155
+ } catch ( e ) {
156
+ throw ( e ) ;
157
+ }
158
+ return callback ( null , fileName ) ;
159
+ } ) ;
160
+ } ,
71
161
72
- _getGeoNamesData : function ( callback ) {
162
+ _parseGeoNamesAdmin2CodesCsv : function ( pathToCsv , callback ) {
163
+ return callback ( ) ;
164
+ } ,
165
+
166
+ _getGeoNamesCititesData : function ( callback ) {
73
167
var now = ( new Date ( ) ) . toISOString ( ) . substr ( 0 , 10 ) ;
74
- if ( fs . existsSync ( GEONAMES_DUMP + '/' + now + '.csv' ) ) {
75
- DEBUG && console . log ( 'Using cached GeoNames data from ' + GEONAMES_URL +
76
- GEONAMES_FILE + '.zip' ) ;
77
- return callback ( null , GEONAMES_DUMP + '/' + now + '.csv' ) ;
168
+ if ( fs . existsSync ( GEONAMES_DUMP + '/cities/' + CITIES_FILE + '_' + now +
169
+ '.csv' ) ) {
170
+ DEBUG && console . log ( 'Using cached GeoNames cities data from ' +
171
+ GEONAMES_URL + CITIES_FILE + '.zip' ) ;
172
+ return callback ( null , GEONAMES_DUMP + '/cities/' + CITIES_FILE + '_' +
173
+ now + '.csv' ) ;
78
174
}
79
- DEBUG && console . log ( 'Getting GeoNames data from ' + GEONAMES_URL +
80
- GEONAMES_FILE + '.zip (this may take a while)' ) ;
175
+ DEBUG && console . log ( 'Getting GeoNames cities data from ' + GEONAMES_URL +
176
+ CITIES_FILE + '.zip (this may take a while)' ) ;
81
177
var options = {
82
- url : GEONAMES_URL + GEONAMES_FILE + '.zip' ,
178
+ url : GEONAMES_URL + CITIES_FILE + '.zip' ,
83
179
encoding : null
84
180
} ;
85
181
request . get ( options , function ( err , response , body ) {
86
182
if ( err || response . statusCode !== 200 ) {
87
- return callback ( 'Error downloading GeoNames data' +
183
+ return callback ( 'Error downloading GeoNames cities data' +
88
184
( err ? ': ' + err : '' ) ) ;
89
185
}
90
- DEBUG && console . log ( 'Received zipped GeoNames data' ) ;
186
+ DEBUG && console . log ( 'Received zipped GeoNames cities data' ) ;
91
187
// Store a dump locally
92
- if ( ! fs . existsSync ( GEONAMES_DUMP ) ) {
93
- fs . mkdirSync ( GEONAMES_DUMP ) ;
188
+ if ( ! fs . existsSync ( GEONAMES_DUMP + '/cities' ) ) {
189
+ fs . mkdirSync ( GEONAMES_DUMP + '/cities' ) ;
94
190
}
95
- var oldName = GEONAMES_DUMP + '/' + GEONAMES_FILE + '.txt' ;
191
+ var oldName = GEONAMES_DUMP + '/cities/ ' + CITIES_FILE + '.txt' ;
96
192
// Name files like a timestamp so we can easily remove old files
97
- var newName = GEONAMES_DUMP + '/' + now + '.csv' ;
98
- var fileName = GEONAMES_DUMP + '/' + now + '.zip' ;
193
+ var newName = GEONAMES_DUMP + '/cities/' + CITIES_FILE + '_' + now +
194
+ '.csv' ;
195
+ var fileName = GEONAMES_DUMP + '/cities/' + CITIES_FILE + '_' + now +
196
+ '.zip' ;
99
197
try {
100
198
fs . writeFileSync ( fileName , body ) ;
101
199
var zipped = new zip ( fileName ) ;
102
- zipped . extractEntryTo ( GEONAMES_FILE + '.txt' , GEONAMES_DUMP , false ,
103
- true ) ;
200
+ zipped . extractEntryTo ( CITIES_FILE + '.txt' , GEONAMES_DUMP + '/cities' ,
201
+ false , true ) ;
104
202
fs . renameSync ( oldName , newName ) ;
105
- fs . unlink ( GEONAMES_DUMP + '/' + now + '.zip' ) ;
106
- DEBUG && console . log ( 'Unzipped GeoNames data' ) ;
203
+ fs . unlink ( GEONAMES_DUMP + '/cities/' + CITIES_FILE + '_' + now +
204
+ '.zip' ) ;
205
+ DEBUG && console . log ( 'Unzipped GeoNames cities data' ) ;
107
206
// Housekeeping, remove old files
108
- var currentFileName = now + '.csv' ;
109
- fs . readdirSync ( GEONAMES_DUMP ) . forEach ( function ( file ) {
207
+ var currentFileName = CITIES_FILE + '_' + now + '.csv' ;
208
+ fs . readdirSync ( GEONAMES_DUMP + '/cities' ) . forEach ( function ( file ) {
110
209
if ( file !== currentFileName ) {
111
- fs . unlink ( GEONAMES_DUMP + '/' + file ) ;
210
+ fs . unlink ( GEONAMES_DUMP + '/cities/ ' + file ) ;
112
211
}
113
212
} ) ;
114
213
} catch ( e ) {
@@ -118,7 +217,7 @@ var geocoder = {
118
217
} ) ;
119
218
} ,
120
219
121
- _parseGeoNamesCsv : function ( pathToCsv , callback ) {
220
+ _parseGeoNamesCitiesCsv : function ( pathToCsv , callback ) {
122
221
var data = [ ] ;
123
222
var lenI = GEONAMES_COLUMNS . length ;
124
223
var that = this ;
@@ -166,10 +265,45 @@ var geocoder = {
166
265
167
266
_init : function ( callback ) {
168
267
DEBUG && console . log ( 'Initializing local reverse geocoder' ) ;
169
- async . waterfall ( [
170
- this . _getGeoNamesData . bind ( this ) ,
171
- this . _parseGeoNamesCsv . bind ( this )
172
- ] , function ( ) {
268
+ // Create local cache folder
269
+ if ( ! fs . existsSync ( GEONAMES_DUMP ) ) {
270
+ fs . mkdirSync ( GEONAMES_DUMP ) ;
271
+ }
272
+ var that = this ;
273
+ async . parallel ( [
274
+ // Get GeoNames cities
275
+ function ( waterfallCallback ) {
276
+ async . waterfall ( [
277
+ that . _getGeoNamesCititesData . bind ( that ) ,
278
+ that . _parseGeoNamesCitiesCsv . bind ( that )
279
+ ] , function ( ) {
280
+ return waterfallCallback ( ) ;
281
+ } ) ;
282
+ } ,
283
+ // Get GeoNames admin 1 codes
284
+ function ( waterfallCallback ) {
285
+ async . waterfall ( [
286
+ that . _getGeoNamesAdmin1CodesData . bind ( that ) ,
287
+ that . _parseGeoNamesAdmin1CodesCsv . bind ( that )
288
+ ] , function ( ) {
289
+ return waterfallCallback ( ) ;
290
+ } ) ;
291
+ } ,
292
+ // Get GeoNames admin 2 codes
293
+ function ( waterfallCallback ) {
294
+ async . waterfall ( [
295
+ that . _getGeoNamesAdmin2CodesData . bind ( that ) ,
296
+ that . _parseGeoNamesAdmin2CodesCsv . bind ( that )
297
+ ] , function ( ) {
298
+ return waterfallCallback ( ) ;
299
+ } ) ;
300
+ }
301
+ ] ,
302
+ // Main callback
303
+ function ( err ) {
304
+ if ( err ) {
305
+ throw ( err ) ;
306
+ }
173
307
return callback ( ) ;
174
308
} ) ;
175
309
} ,
@@ -195,6 +329,7 @@ var geocoder = {
195
329
async . series (
196
330
functions ,
197
331
function ( err , results ) {
332
+ DEBUG && console . log ( 'Delivering results' ) ;
198
333
return callback ( null , results ) ;
199
334
} ) ;
200
335
}
0 commit comments