StreetAddress 1.0.6 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +48 -0
- data/LICENCE +21 -0
- data/README.md +87 -42
- data/lib/street_address.rb +710 -695
- metadata +87 -19
- data/.gitignore +0 -6
- data/Gemfile +0 -7
- data/Rakefile +0 -8
- data/street_address.gemspec +0 -21
- data/test/test_street_address.rb +0 -203
data/lib/street_address.rb
CHANGED
|
@@ -1,70 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
=== Usage:
|
|
4
|
-
StreetAddress::US.parse("1600 Pennsylvania Ave, washington, dc")
|
|
5
|
-
|
|
6
|
-
=== Valid Address Formats
|
|
7
|
-
|
|
8
|
-
1600 Pennsylvania Ave Washington DC 20006
|
|
9
|
-
1600 Pennsylvania Ave #400, Washington, DC, 20006
|
|
10
|
-
1600 Pennsylvania Ave Washington, DC
|
|
11
|
-
1600 Pennsylvania Ave #400 Washington DC
|
|
12
|
-
1600 Pennsylvania Ave, 20006
|
|
13
|
-
1600 Pennsylvania Ave #400, 20006
|
|
14
|
-
1600 Pennsylvania Ave 20006
|
|
15
|
-
1600 Pennsylvania Ave #400 20006
|
|
16
|
-
|
|
17
|
-
=== Valid Intersection Formats
|
|
18
|
-
|
|
19
|
-
Hollywood & Vine, Los Angeles, CA
|
|
20
|
-
Hollywood Blvd and Vine St, Los Angeles, CA
|
|
21
|
-
Mission Street at Valencia Street, San Francisco, CA
|
|
22
|
-
Hollywood & Vine, Los Angeles, CA, 90028
|
|
23
|
-
Hollywood Blvd and Vine St, Los Angeles, CA, 90028
|
|
24
|
-
Mission Street at Valencia Street, San Francisco, CA, 90028
|
|
25
|
-
|
|
26
|
-
==== License
|
|
27
|
-
|
|
28
|
-
Copyright (c) 2007 Riderway (Derrek Long, Nicholas Schlueter)
|
|
29
|
-
|
|
30
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
31
|
-
a copy of this software and associated documentation files (the
|
|
32
|
-
"Software"), to deal in the Software without restriction, including
|
|
33
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
34
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
35
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
36
|
-
the following conditions:
|
|
37
|
-
|
|
38
|
-
The above copyright notice and this permission notice shall be
|
|
39
|
-
included in all copies or substantial portions of the Software.
|
|
40
|
-
|
|
41
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
42
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
43
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
44
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
45
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
46
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
47
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
48
|
-
|
|
49
|
-
==== Notes
|
|
50
|
-
If parts of the address are omitted from the original string
|
|
51
|
-
the accessor will be nil in StreetAddress::US::Address.
|
|
52
|
-
|
|
53
|
-
Example:
|
|
54
|
-
address = StreetAddress::US.parse("1600 Pennsylvania Ave, washington, dc")
|
|
55
|
-
assert address.postal_code.nil?
|
|
56
|
-
|
|
57
|
-
==== Acknowledgements
|
|
58
|
-
|
|
59
|
-
This gem is a near direct port of the perl module Geo::StreetAddress::US
|
|
60
|
-
originally written by Schuyler D. Erle. For more information see
|
|
61
|
-
http://search.cpan.org/~sderle/Geo-StreetAddress-US-0.99/
|
|
62
|
-
|
|
63
|
-
=end
|
|
1
|
+
# frozen_string_literal: true
|
|
64
2
|
|
|
65
3
|
module StreetAddress
|
|
66
4
|
class US
|
|
67
|
-
VERSION = '
|
|
5
|
+
VERSION = '2.0.0'
|
|
6
|
+
|
|
68
7
|
DIRECTIONAL = {
|
|
69
8
|
"north" => "N",
|
|
70
9
|
"northeast" => "NE",
|
|
@@ -74,379 +13,381 @@ module StreetAddress
|
|
|
74
13
|
"southwest" => "SW",
|
|
75
14
|
"west" => "W",
|
|
76
15
|
"northwest" => "NW"
|
|
77
|
-
}
|
|
78
|
-
DIRECTION_CODES = DIRECTIONAL.invert
|
|
16
|
+
}.freeze
|
|
17
|
+
DIRECTION_CODES = DIRECTIONAL.invert.freeze
|
|
79
18
|
|
|
80
19
|
STREET_TYPES = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
20
|
+
'allee' => "aly",
|
|
21
|
+
'alley' => "aly",
|
|
22
|
+
'ally' => "aly",
|
|
23
|
+
'anex' => "anx",
|
|
24
|
+
'annex' => "anx",
|
|
25
|
+
'annx' => "anx",
|
|
26
|
+
'arcade'=> "arc",
|
|
27
|
+
'av' => "ave",
|
|
28
|
+
'aven' => "ave",
|
|
29
|
+
'avenu' => "ave",
|
|
30
|
+
'avenue'=> "ave",
|
|
31
|
+
'avn' => "ave",
|
|
32
|
+
'avnue' => "ave",
|
|
33
|
+
'bayoo' => "byu",
|
|
34
|
+
'bayou' => "byu",
|
|
35
|
+
'beach' => "bch",
|
|
36
|
+
'bend' => "bnd",
|
|
37
|
+
'bluf' => "blf",
|
|
38
|
+
'bluff' => "blf",
|
|
39
|
+
'bluffs'=> "blfs",
|
|
40
|
+
'bot' => "btm",
|
|
41
|
+
'bottm' => "btm",
|
|
42
|
+
'bottom'=> "btm",
|
|
43
|
+
'boul' => "blvd",
|
|
44
|
+
'boulevard' => "blvd",
|
|
45
|
+
'boulv' => "blvd",
|
|
46
|
+
'branch'=> "br",
|
|
47
|
+
'brdge' => "brg",
|
|
48
|
+
'bridge'=> "brg",
|
|
49
|
+
'brnch' => "br",
|
|
50
|
+
'brook' => "brk",
|
|
51
|
+
'brooks'=> "brks",
|
|
52
|
+
'burg' => "bg",
|
|
53
|
+
'burgs' => "bgs",
|
|
54
|
+
'bypa' => "byp",
|
|
55
|
+
'bypas' => "byp",
|
|
56
|
+
'bypass'=> "byp",
|
|
57
|
+
'byps' => "byp",
|
|
58
|
+
'camp' => "cp",
|
|
59
|
+
'canyn' => "cyn",
|
|
60
|
+
'canyon'=> "cyn",
|
|
61
|
+
'cape' => "cpe",
|
|
62
|
+
'causeway' => "cswy",
|
|
63
|
+
'causway' => "cswy",
|
|
64
|
+
'cen' => "ctr",
|
|
65
|
+
'cent' => "ctr",
|
|
66
|
+
'center'=> "ctr",
|
|
67
|
+
'centers' => "ctrs",
|
|
68
|
+
'centr' => "ctr",
|
|
69
|
+
'centre'=> "ctr",
|
|
70
|
+
'circ' => "cir",
|
|
71
|
+
'circl' => "cir",
|
|
72
|
+
'circle'=> "cir",
|
|
73
|
+
'circles' => "cirs",
|
|
74
|
+
'ck' => "crk",
|
|
75
|
+
'cliff' => "clf",
|
|
76
|
+
'cliffs'=> "clfs",
|
|
77
|
+
'club' => "clb",
|
|
78
|
+
'cmp' => "cp",
|
|
79
|
+
'cnter' => "ctr",
|
|
80
|
+
'cntr' => "ctr",
|
|
81
|
+
'cnyn' => "cyn",
|
|
82
|
+
'common'=> "cmn",
|
|
83
|
+
'corner'=> "cor",
|
|
84
|
+
'corners' => "cors",
|
|
85
|
+
'course'=> "crse",
|
|
86
|
+
'court' => "ct",
|
|
87
|
+
'courts'=> "cts",
|
|
88
|
+
'cove' => "cv",
|
|
89
|
+
'coves' => "cvs",
|
|
90
|
+
'cr' => "crk",
|
|
91
|
+
'crcl' => "cir",
|
|
92
|
+
'crcle' => "cir",
|
|
93
|
+
'crecent' => "cres",
|
|
94
|
+
'creek' => "crk",
|
|
95
|
+
'crescent' => "cres",
|
|
96
|
+
'cresent' => "cres",
|
|
97
|
+
'crest' => "crst",
|
|
98
|
+
'crossing' => "xing",
|
|
99
|
+
'crossroad' => "xrd",
|
|
100
|
+
'crscnt'=> "cres",
|
|
101
|
+
'crsent'=> "cres",
|
|
102
|
+
'crsnt' => "cres",
|
|
103
|
+
'crssing' => "xing",
|
|
104
|
+
'crssng'=> "xing",
|
|
105
|
+
'crt' => "ct",
|
|
106
|
+
'curve' => "curv",
|
|
107
|
+
'dale' => "dl",
|
|
108
|
+
'dam' => "dm",
|
|
109
|
+
'div' => "dv",
|
|
110
|
+
'divide'=> "dv",
|
|
111
|
+
'driv' => "dr",
|
|
112
|
+
'drive' => "dr",
|
|
113
|
+
'drives'=> "drs",
|
|
114
|
+
'drv' => "dr",
|
|
115
|
+
'dvd' => "dv",
|
|
116
|
+
'estate'=> "est",
|
|
117
|
+
'estates' => "ests",
|
|
118
|
+
'exp' => "expy",
|
|
119
|
+
'expr' => "expy",
|
|
120
|
+
'express' => "expy",
|
|
121
|
+
'expressway' => "expy",
|
|
122
|
+
'expw' => "expy",
|
|
123
|
+
'extension' => "ext",
|
|
124
|
+
'extensions' => "exts",
|
|
125
|
+
'extn' => "ext",
|
|
126
|
+
'extnsn'=> "ext",
|
|
127
|
+
'falls' => "fls",
|
|
128
|
+
'ferry' => "fry",
|
|
129
|
+
'field' => "fld",
|
|
130
|
+
'fields'=> "flds",
|
|
131
|
+
'flat' => "flt",
|
|
132
|
+
'flats' => "flts",
|
|
133
|
+
'ford' => "frd",
|
|
134
|
+
'fords' => "frds",
|
|
135
|
+
'forest'=> "frst",
|
|
136
|
+
'forests' => "frst",
|
|
137
|
+
'forg' => "frg",
|
|
138
|
+
'forge' => "frg",
|
|
139
|
+
'forges'=> "frgs",
|
|
140
|
+
'fork' => "frk",
|
|
141
|
+
'forks' => "frks",
|
|
142
|
+
'fort' => "ft",
|
|
143
|
+
'freeway' => "fwy",
|
|
144
|
+
'freewy'=> "fwy",
|
|
145
|
+
'frry' => "fry",
|
|
146
|
+
'frt' => "ft",
|
|
147
|
+
'frway' => "fwy",
|
|
148
|
+
'frwy' => "fwy",
|
|
149
|
+
'garden'=> "gdn",
|
|
150
|
+
'gardens' => "gdns",
|
|
151
|
+
'gardn' => "gdn",
|
|
152
|
+
'gateway' => "gtwy",
|
|
153
|
+
'gatewy'=> "gtwy",
|
|
154
|
+
'gatway'=> "gtwy",
|
|
155
|
+
'glen' => "gln",
|
|
156
|
+
'glens' => "glns",
|
|
157
|
+
'grden' => "gdn",
|
|
158
|
+
'grdn' => "gdn",
|
|
159
|
+
'grdns' => "gdns",
|
|
160
|
+
'green' => "grn",
|
|
161
|
+
'greens'=> "grns",
|
|
162
|
+
'grov' => "grv",
|
|
163
|
+
'grove' => "grv",
|
|
164
|
+
'groves'=> "grvs",
|
|
165
|
+
'gtway' => "gtwy",
|
|
166
|
+
'harb' => "hbr",
|
|
167
|
+
'harbor'=> "hbr",
|
|
168
|
+
'harbors' => "hbrs",
|
|
169
|
+
'harbr' => "hbr",
|
|
170
|
+
'haven' => "hvn",
|
|
171
|
+
'havn' => "hvn",
|
|
172
|
+
'height'=> "hts",
|
|
173
|
+
'heights' => "hts",
|
|
174
|
+
'hgts' => "hts",
|
|
175
|
+
'highway' => "hwy",
|
|
176
|
+
'highwy'=> "hwy",
|
|
177
|
+
'hill' => "hl",
|
|
178
|
+
'hills' => "hls",
|
|
179
|
+
'hiway' => "hwy",
|
|
180
|
+
'hiwy' => "hwy",
|
|
181
|
+
'hllw' => "holw",
|
|
182
|
+
'hollow'=> "holw",
|
|
183
|
+
'hollows' => "holw",
|
|
184
|
+
'holws' => "holw",
|
|
185
|
+
'hrbor' => "hbr",
|
|
186
|
+
'ht' => "hts",
|
|
187
|
+
'hway' => "hwy",
|
|
188
|
+
'inlet' => "inlt",
|
|
189
|
+
'island'=> "is",
|
|
190
|
+
'islands' => "iss",
|
|
191
|
+
'isles' => "isle",
|
|
192
|
+
'islnd' => "is",
|
|
193
|
+
'islnds'=> "iss",
|
|
194
|
+
'jction'=> "jct",
|
|
195
|
+
'jctn' => "jct",
|
|
196
|
+
'jctns' => "jcts",
|
|
197
|
+
'junction' => "jct",
|
|
198
|
+
'junctions' => "jcts",
|
|
199
|
+
'junctn'=> "jct",
|
|
200
|
+
'juncton' => "jct",
|
|
201
|
+
'key' => "ky",
|
|
202
|
+
'keys' => "kys",
|
|
203
|
+
'knol' => "knl",
|
|
204
|
+
'knoll' => "knl",
|
|
205
|
+
'knolls'=> "knls",
|
|
206
|
+
'la' => "ln",
|
|
207
|
+
'lake' => "lk",
|
|
208
|
+
'lakes' => "lks",
|
|
209
|
+
'landing' => "lndg",
|
|
210
|
+
'lane' => "ln",
|
|
211
|
+
'lanes' => "ln",
|
|
212
|
+
'ldge' => "ldg",
|
|
213
|
+
'light' => "lgt",
|
|
214
|
+
'lights'=> "lgts",
|
|
215
|
+
'lndng' => "lndg",
|
|
216
|
+
'loaf' => "lf",
|
|
217
|
+
'lock' => "lck",
|
|
218
|
+
'locks' => "lcks",
|
|
219
|
+
'lodg' => "ldg",
|
|
220
|
+
'lodge' => "ldg",
|
|
221
|
+
'loops' => "loop",
|
|
222
|
+
'manor' => "mnr",
|
|
223
|
+
'manors'=> "mnrs",
|
|
224
|
+
'meadow'=> "mdw",
|
|
225
|
+
'meadows' => "mdws",
|
|
226
|
+
'medows'=> "mdws",
|
|
227
|
+
'mill' => "ml",
|
|
228
|
+
'mills' => "mls",
|
|
229
|
+
'mission' => "msn",
|
|
230
|
+
'missn' => "msn",
|
|
231
|
+
'mnt' => "mt",
|
|
232
|
+
'mntain'=> "mtn",
|
|
233
|
+
'mntn' => "mtn",
|
|
234
|
+
'mntns' => "mtns",
|
|
235
|
+
'motorway' => "mtwy",
|
|
236
|
+
'mount' => "mt",
|
|
237
|
+
'mountain' => "mtn",
|
|
238
|
+
'mountains' => "mtns",
|
|
239
|
+
'mountin' => "mtn",
|
|
240
|
+
'mssn' => "msn",
|
|
241
|
+
'mtin' => "mtn",
|
|
242
|
+
'neck' => "nck",
|
|
243
|
+
'orchard' => "orch",
|
|
244
|
+
'orchrd'=> "orch",
|
|
245
|
+
'overpass' => "opas",
|
|
246
|
+
'ovl' => "oval",
|
|
247
|
+
'parks' => "park",
|
|
248
|
+
'parkway' => "pkwy",
|
|
249
|
+
'parkways' => "pkwy",
|
|
250
|
+
'parkwy'=> "pkwy",
|
|
251
|
+
'passage' => "psge",
|
|
252
|
+
'paths' => "path",
|
|
253
|
+
'pikes' => "pike",
|
|
254
|
+
'pine' => "pne",
|
|
255
|
+
'pines' => "pnes",
|
|
256
|
+
'pk' => "park",
|
|
257
|
+
'pkway' => "pkwy",
|
|
258
|
+
'pkwys' => "pkwy",
|
|
259
|
+
'pky' => "pkwy",
|
|
260
|
+
'place' => "pl",
|
|
261
|
+
'plain' => "pln",
|
|
262
|
+
'plaines' => "plns",
|
|
263
|
+
'plains'=> "plns",
|
|
264
|
+
'plaza' => "plz",
|
|
265
|
+
'plza' => "plz",
|
|
266
|
+
'point' => "pt",
|
|
267
|
+
'points'=> "pts",
|
|
268
|
+
'port' => "prt",
|
|
269
|
+
'ports' => "prts",
|
|
270
|
+
'prairie' => "pr",
|
|
271
|
+
'prarie'=> "pr",
|
|
272
|
+
'prk' => "park",
|
|
273
|
+
'prr' => "pr",
|
|
274
|
+
'rad' => "radl",
|
|
275
|
+
'radial'=> "radl",
|
|
276
|
+
'radiel'=> "radl",
|
|
277
|
+
'ranch' => "rnch",
|
|
278
|
+
'ranches' => "rnch",
|
|
279
|
+
'rapid' => "rpd",
|
|
280
|
+
'rapids'=> "rpds",
|
|
281
|
+
'rdge' => "rdg",
|
|
282
|
+
'rest' => "rst",
|
|
283
|
+
'ridge' => "rdg",
|
|
284
|
+
'ridges'=> "rdgs",
|
|
285
|
+
'river' => "riv",
|
|
286
|
+
'rivr' => "riv",
|
|
287
|
+
'rnchs' => "rnch",
|
|
288
|
+
'road' => "rd",
|
|
289
|
+
'roads' => "rds",
|
|
290
|
+
'route' => "rte",
|
|
291
|
+
'rvr' => "riv",
|
|
292
|
+
'shoal' => "shl",
|
|
293
|
+
'shoals'=> "shls",
|
|
294
|
+
'shoar' => "shr",
|
|
295
|
+
'shoars'=> "shrs",
|
|
296
|
+
'shore' => "shr",
|
|
297
|
+
'shores'=> "shrs",
|
|
298
|
+
'skyway'=> "skwy",
|
|
299
|
+
'spng' => "spg",
|
|
300
|
+
'spngs' => "spgs",
|
|
301
|
+
'spring'=> "spg",
|
|
302
|
+
'springs' => "spgs",
|
|
303
|
+
'sprng' => "spg",
|
|
304
|
+
'sprngs'=> "spgs",
|
|
305
|
+
'spurs' => "spur",
|
|
306
|
+
'sqr' => "sq",
|
|
307
|
+
'sqre' => "sq",
|
|
308
|
+
'sqrs' => "sqs",
|
|
309
|
+
'squ' => "sq",
|
|
310
|
+
'square'=> "sq",
|
|
311
|
+
'squares' => "sqs",
|
|
312
|
+
'station' => "sta",
|
|
313
|
+
'statn' => "sta",
|
|
314
|
+
'stn' => "sta",
|
|
315
|
+
'str' => "st",
|
|
316
|
+
'strav' => "stra",
|
|
317
|
+
'strave'=> "stra",
|
|
318
|
+
'straven' => "stra",
|
|
319
|
+
'stravenue' => "stra",
|
|
320
|
+
'stravn'=> "stra",
|
|
321
|
+
'stream'=> "strm",
|
|
322
|
+
'street'=> "st",
|
|
323
|
+
'streets' => "sts",
|
|
324
|
+
'streme'=> "strm",
|
|
325
|
+
'strt' => "st",
|
|
326
|
+
'strvn' => "stra",
|
|
327
|
+
'strvnue' => "stra",
|
|
328
|
+
'sumit' => "smt",
|
|
329
|
+
'sumitt'=> "smt",
|
|
330
|
+
'summit'=> "smt",
|
|
331
|
+
'terr' => "ter",
|
|
332
|
+
'terrace' => "ter",
|
|
333
|
+
'throughway' => "trwy",
|
|
334
|
+
'tpk' => "tpke",
|
|
335
|
+
'tr' => "trl",
|
|
336
|
+
'trace' => "trce",
|
|
337
|
+
'traces'=> "trce",
|
|
338
|
+
'track' => "trak",
|
|
339
|
+
'tracks'=> "trak",
|
|
340
|
+
'trafficway' => "trfy",
|
|
341
|
+
'trail' => "trl",
|
|
342
|
+
'trails'=> "trl",
|
|
343
|
+
'trk' => "trak",
|
|
344
|
+
'trks' => "trak",
|
|
345
|
+
'trls' => "trl",
|
|
346
|
+
'trnpk' => "tpke",
|
|
347
|
+
'trpk' => "tpke",
|
|
348
|
+
'tunel' => "tunl",
|
|
349
|
+
'tunls' => "tunl",
|
|
350
|
+
'tunnel'=> "tunl",
|
|
351
|
+
'tunnels' => "tunl",
|
|
352
|
+
'tunnl' => "tunl",
|
|
353
|
+
'turnpike' => "tpke",
|
|
354
|
+
'turnpk'=> "tpke",
|
|
355
|
+
'underpass' => "upas",
|
|
356
|
+
'union' => "un",
|
|
357
|
+
'unions'=> "uns",
|
|
358
|
+
'valley'=> "vly",
|
|
359
|
+
'valleys' => "vlys",
|
|
360
|
+
'vally' => "vly",
|
|
361
|
+
'vdct' => "via",
|
|
362
|
+
'viadct'=> "via",
|
|
363
|
+
'viaduct' => "via",
|
|
364
|
+
'view' => "vw",
|
|
365
|
+
'views' => "vws",
|
|
366
|
+
'vill' => "vlg",
|
|
367
|
+
'villag'=> "vlg",
|
|
368
|
+
'village' => "vlg",
|
|
369
|
+
'villages' => "vlgs",
|
|
370
|
+
'ville' => "vl",
|
|
371
|
+
'villg' => "vlg",
|
|
372
|
+
'villiage' => "vlg",
|
|
373
|
+
'vist' => "vis",
|
|
374
|
+
'vista' => "vis",
|
|
375
|
+
'vlly' => "vly",
|
|
376
|
+
'vst' => "vis",
|
|
377
|
+
'vsta' => "vis",
|
|
378
|
+
'walks' => "walk",
|
|
379
|
+
'well' => "wl",
|
|
380
|
+
'wells' => "wls",
|
|
381
|
+
'wy' => "way",
|
|
382
|
+
}.freeze
|
|
383
|
+
|
|
384
|
+
STREET_TYPES_LIST = begin
|
|
385
|
+
list = {}
|
|
386
|
+
STREET_TYPES.each_pair do |key, val|
|
|
387
|
+
list[key] = true
|
|
388
|
+
list[val] = true
|
|
389
|
+
end
|
|
390
|
+
list.freeze
|
|
450
391
|
end
|
|
451
392
|
|
|
452
393
|
STATE_CODES = {
|
|
@@ -509,9 +450,9 @@ module StreetAddress
|
|
|
509
450
|
"west virginia" => "WV",
|
|
510
451
|
"wisconsin" => "WI",
|
|
511
452
|
"wyoming" => "WY"
|
|
512
|
-
}
|
|
453
|
+
}.freeze
|
|
513
454
|
|
|
514
|
-
STATE_NAMES = STATE_CODES.invert
|
|
455
|
+
STATE_NAMES = STATE_CODES.invert.freeze
|
|
515
456
|
|
|
516
457
|
STATE_FIPS = {
|
|
517
458
|
"01" => "AL",
|
|
@@ -567,252 +508,309 @@ module StreetAddress
|
|
|
567
508
|
"56" => "WY",
|
|
568
509
|
"72" => "PR",
|
|
569
510
|
"78" => "VI"
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
FIPS_STATES = STATE_FIPS.invert
|
|
511
|
+
}.freeze
|
|
512
|
+
|
|
513
|
+
FIPS_STATES = STATE_FIPS.invert.freeze
|
|
514
|
+
|
|
515
|
+
NORMALIZE_MAP = {
|
|
516
|
+
'prefix' => DIRECTIONAL,
|
|
517
|
+
'prefix1' => DIRECTIONAL,
|
|
518
|
+
'prefix2' => DIRECTIONAL,
|
|
519
|
+
'suffix' => DIRECTIONAL,
|
|
520
|
+
'suffix1' => DIRECTIONAL,
|
|
521
|
+
'suffix2' => DIRECTIONAL,
|
|
522
|
+
'street_type' => STREET_TYPES,
|
|
523
|
+
'street_type1' => STREET_TYPES,
|
|
524
|
+
'street_type2' => STREET_TYPES,
|
|
525
|
+
'state' => STATE_CODES,
|
|
526
|
+
}.freeze
|
|
573
527
|
|
|
574
528
|
class << self
|
|
575
529
|
attr_accessor(
|
|
576
530
|
:street_type_regexp,
|
|
531
|
+
:street_type_matches,
|
|
577
532
|
:number_regexp,
|
|
578
533
|
:fraction_regexp,
|
|
579
534
|
:state_regexp,
|
|
580
535
|
:city_and_state_regexp,
|
|
581
|
-
:direct_regexp,
|
|
536
|
+
:direct_regexp,
|
|
582
537
|
:zip_regexp,
|
|
583
538
|
:corner_regexp,
|
|
584
|
-
:unit_regexp,
|
|
585
539
|
:street_regexp,
|
|
586
540
|
:place_regexp,
|
|
587
541
|
:address_regexp,
|
|
588
|
-
:informal_address_regexp
|
|
542
|
+
:informal_address_regexp,
|
|
543
|
+
:dircode_regexp,
|
|
544
|
+
:unit_prefix_numbered_regexp,
|
|
545
|
+
:unit_prefix_unnumbered_regexp,
|
|
546
|
+
:unit_regexp,
|
|
547
|
+
:sep_regexp,
|
|
548
|
+
:sep_avoid_unit_regexp,
|
|
549
|
+
:intersection_regexp
|
|
589
550
|
)
|
|
590
551
|
end
|
|
591
552
|
|
|
592
|
-
self.
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
553
|
+
self.street_type_matches = {}
|
|
554
|
+
STREET_TYPES.each_pair { |type,abbrv|
|
|
555
|
+
self.street_type_matches[abbrv] = /\b (?: #{abbrv}|#{Regexp.quote(type)} ) \b/ix
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
self.street_type_regexp = Regexp.new(STREET_TYPES_LIST.keys.join("|"), Regexp::IGNORECASE)
|
|
559
|
+
self.fraction_regexp = /\d+\/\d+/
|
|
560
|
+
self.state_regexp = Regexp.new(
|
|
561
|
+
'\b' + STATE_CODES.flatten.map{ |code| Regexp.quote(code) }.join("|") + '\b',
|
|
562
|
+
Regexp::IGNORECASE
|
|
563
|
+
)
|
|
564
|
+
self.direct_regexp = Regexp.new(
|
|
565
|
+
(DIRECTIONAL.keys +
|
|
566
|
+
DIRECTIONAL.values.sort { |a,b|
|
|
567
|
+
b.length <=> a.length
|
|
568
|
+
}.map { |c|
|
|
569
|
+
f = c.gsub(/(\w)/, '\1.')
|
|
570
|
+
[Regexp::quote(f), Regexp::quote(c)]
|
|
571
|
+
}
|
|
572
|
+
).join("|"),
|
|
573
|
+
Regexp::IGNORECASE
|
|
574
|
+
)
|
|
575
|
+
self.dircode_regexp = Regexp.new(DIRECTION_CODES.keys.join("|"), Regexp::IGNORECASE)
|
|
576
|
+
self.zip_regexp = /(?:(?<postal_code>\d{5})(?:-?(?<postal_code_ext>\d{4}))?)/
|
|
577
|
+
self.corner_regexp = /(?:\band\b|\bat\b|&|\@)/i
|
|
578
|
+
|
|
579
|
+
# we don't include letters in the number regex because we want to
|
|
580
|
+
# treat "42S" as "42 S" (42 South). For example,
|
|
581
|
+
# Utah and Wisconsin have a more elaborate system of block numbering
|
|
582
|
+
# http://en.wikipedia.org/wiki/House_number#Block_numbers
|
|
583
|
+
self.number_regexp = /(?<number>\d+-?\d*)(?=\D)/ix
|
|
584
|
+
|
|
585
|
+
# note that expressions like [^,]+ may scan more than you expect
|
|
586
|
+
self.street_regexp = /
|
|
597
587
|
(?:
|
|
598
|
-
|
|
599
|
-
(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
f = x.gsub(/(\w)/, '\1.')
|
|
608
|
-
[Regexp::quote(f), Regexp::quote(x)]
|
|
609
|
-
}.join("|")
|
|
610
|
-
self.zip_regexp = '(\d{5})(?:-?(\d{4})?)'
|
|
611
|
-
self.corner_regexp = '(?:\band\b|\bat\b|&|\@)'
|
|
612
|
-
self.unit_regexp = '(?:(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+|(\#)\W*)([\w-]+)'
|
|
613
|
-
self.street_regexp =
|
|
614
|
-
'(?:
|
|
615
|
-
(?:(' + direct_regexp + ')\W+
|
|
616
|
-
(' + street_type_regexp + ')\b)
|
|
588
|
+
# special case for addresses like 100 South Street
|
|
589
|
+
(?:(?<street> #{direct_regexp})\W+
|
|
590
|
+
(?<street_type> #{street_type_regexp})\b
|
|
591
|
+
)
|
|
592
|
+
|
|
|
593
|
+
(?:(?<prefix> #{direct_regexp})\W+)?
|
|
594
|
+
(?:
|
|
595
|
+
(?<street> [^,]*\d)
|
|
596
|
+
(?:[^\w,]* (?<suffix> #{direct_regexp})\b)
|
|
617
597
|
|
|
|
618
|
-
(
|
|
619
|
-
(?:
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
598
|
+
(?<street> [^,]+)
|
|
599
|
+
(?:[^\w,]+(?<street_type> #{street_type_regexp})\b)
|
|
600
|
+
(?:[^\w,]+(?<suffix> #{direct_regexp})\b)?
|
|
601
|
+
|
|
|
602
|
+
(?<street> [^,]+?)
|
|
603
|
+
(?:[^\w,]+(?<street_type> #{street_type_regexp})\b)?
|
|
604
|
+
(?:[^\w,]+(?<suffix> #{direct_regexp})\b)?
|
|
605
|
+
)
|
|
606
|
+
)
|
|
607
|
+
/ix;
|
|
608
|
+
|
|
609
|
+
# http://pe.usps.com/text/pub28/pub28c2_003.htm
|
|
610
|
+
# TODO add support for those that don't require a number
|
|
611
|
+
# TODO map to standard names/abbreviations
|
|
612
|
+
self.unit_prefix_numbered_regexp = /
|
|
613
|
+
(?<unit_prefix>
|
|
614
|
+
su?i?te
|
|
615
|
+
|p\W*[om]\W*b(?:ox)?
|
|
616
|
+
|(?:ap|dep)(?:ar)?t(?:me?nt)?
|
|
617
|
+
|ro*m
|
|
618
|
+
|flo*r?
|
|
619
|
+
|uni?t
|
|
620
|
+
|bu?i?ldi?n?g
|
|
621
|
+
|ha?nga?r
|
|
622
|
+
|lo?t
|
|
623
|
+
|pier
|
|
624
|
+
|slip
|
|
625
|
+
|spa?ce?
|
|
626
|
+
|stop
|
|
627
|
+
|tra?i?le?r
|
|
628
|
+
|box)(?![a-z])
|
|
629
|
+
/ix;
|
|
630
|
+
|
|
631
|
+
self.unit_prefix_unnumbered_regexp = /
|
|
632
|
+
(?<unit_prefix>
|
|
633
|
+
ba?se?me?n?t
|
|
634
|
+
|fro?nt
|
|
635
|
+
|lo?bby
|
|
636
|
+
|lowe?r
|
|
637
|
+
|off?i?ce?
|
|
638
|
+
|pe?n?t?ho?u?s?e?
|
|
639
|
+
|rear
|
|
640
|
+
|side
|
|
641
|
+
|uppe?r
|
|
642
|
+
)\b
|
|
643
|
+
/ix;
|
|
644
|
+
|
|
645
|
+
self.unit_regexp = /
|
|
646
|
+
(?:
|
|
647
|
+
(?: (?:#{unit_prefix_numbered_regexp} \W*)
|
|
648
|
+
| (?<unit_prefix> \#)\W*
|
|
630
649
|
)
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
650
|
+
(?<unit> [\w-]+)
|
|
651
|
+
)
|
|
652
|
+
|
|
|
653
|
+
#{unit_prefix_unnumbered_regexp}
|
|
654
|
+
/ix;
|
|
655
|
+
|
|
656
|
+
self.city_and_state_regexp = /
|
|
657
|
+
(?:
|
|
658
|
+
(?<city> [^\d,]+?)\W+
|
|
659
|
+
(?<state> #{state_regexp})
|
|
660
|
+
)
|
|
661
|
+
/ix;
|
|
662
|
+
|
|
663
|
+
self.place_regexp = /
|
|
664
|
+
(?:#{city_and_state_regexp}\W*)? (?:#{zip_regexp})?
|
|
665
|
+
/ix;
|
|
666
|
+
|
|
667
|
+
self.address_regexp = /
|
|
668
|
+
\A
|
|
669
|
+
[^\w\x23]* # skip non-word chars except # (eg unit)
|
|
670
|
+
#{number_regexp} \W*
|
|
671
|
+
(?:#{fraction_regexp}\W*)?
|
|
672
|
+
#{street_regexp}\W+
|
|
673
|
+
(?:#{unit_regexp}\W+)?
|
|
674
|
+
#{place_regexp}
|
|
675
|
+
\W* # require on non-word chars at end
|
|
676
|
+
\z # right up to end of string
|
|
677
|
+
/ix;
|
|
678
|
+
|
|
679
|
+
self.sep_regexp = /(?:\W+|\Z)/;
|
|
680
|
+
self.sep_avoid_unit_regexp = /(?:[^\#\w]+|\Z)/;
|
|
681
|
+
|
|
682
|
+
self.informal_address_regexp = /
|
|
683
|
+
\A
|
|
684
|
+
\s* # skip leading whitespace
|
|
685
|
+
(?:#{unit_regexp} #{sep_regexp})?
|
|
686
|
+
(?:#{number_regexp})? \W*
|
|
687
|
+
(?:#{fraction_regexp} \W*)?
|
|
688
|
+
#{street_regexp} #{sep_avoid_unit_regexp}
|
|
689
|
+
(?:#{unit_regexp} #{sep_regexp})?
|
|
690
|
+
(?:#{place_regexp})?
|
|
691
|
+
# don't require match to reach end of string
|
|
692
|
+
/ix;
|
|
693
|
+
|
|
694
|
+
self.intersection_regexp = /\A\W*
|
|
695
|
+
#{street_regexp}\W*?
|
|
696
|
+
|
|
697
|
+
\s+#{corner_regexp}\s+
|
|
698
|
+
|
|
699
|
+
# (?{ exists $_{$_} and $_{$_.1} = delete $_{$_} for (qw{prefix street type suffix})})
|
|
700
|
+
#{street_regexp}\W+
|
|
701
|
+
# (?{ exists $_{$_} and $_{$_.2} = delete $_{$_} for (qw{prefix street type suffix})})
|
|
702
|
+
|
|
703
|
+
#{place_regexp}
|
|
704
|
+
\W*\z
|
|
705
|
+
/ix;
|
|
706
|
+
|
|
669
707
|
class << self
|
|
670
|
-
def parse(location, args
|
|
671
|
-
if
|
|
672
|
-
parse_intersection(location)
|
|
673
|
-
elsif args[:informal]
|
|
674
|
-
parse_address(location) || parse_informal_address(location)
|
|
708
|
+
def parse(location, args={})
|
|
709
|
+
if( corner_regexp.match(location) )
|
|
710
|
+
return parse_intersection(location, args)
|
|
675
711
|
else
|
|
676
|
-
parse_address(location)
|
|
712
|
+
return parse_address(location, args) || parse_informal_address(location, args)
|
|
677
713
|
end
|
|
678
714
|
end
|
|
679
|
-
=begin rdoc
|
|
680
|
-
|
|
681
|
-
parses only an intersection and returns an instance of
|
|
682
|
-
StreetAddress::US::Address or nil if the intersection cannot be parsed
|
|
683
|
-
|
|
684
|
-
====example
|
|
685
|
-
address = StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
|
|
686
|
-
assert address.intersection?
|
|
687
|
-
|
|
688
|
-
=end
|
|
689
|
-
def parse_intersection(inter)
|
|
690
|
-
regex = Regexp.new(
|
|
691
|
-
'\A\W*' + street_regexp + '\W*?
|
|
692
|
-
\s+' + corner_regexp + '\s+' +
|
|
693
|
-
street_regexp + '\W+' +
|
|
694
|
-
place_regexp + '\W*\Z', Regexp::IGNORECASE + Regexp::EXTENDED
|
|
695
|
-
)
|
|
696
715
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
normalize_address(
|
|
700
|
-
StreetAddress::US::Address.new(
|
|
701
|
-
:street => match[4] || match[9],
|
|
702
|
-
:street_type => match[5],
|
|
703
|
-
:suffix => match[6],
|
|
704
|
-
:prefix => match[3],
|
|
705
|
-
:street2 => match[15] || match[20],
|
|
706
|
-
:street_type2 => match[16],
|
|
707
|
-
:suffix2 => match[17],
|
|
708
|
-
:prefix2 => match[14],
|
|
709
|
-
:city => match[23],
|
|
710
|
-
:state => match[24],
|
|
711
|
-
:postal_code => match[25]
|
|
712
|
-
)
|
|
713
|
-
)
|
|
714
|
-
end
|
|
716
|
+
def parse_address(address, args={})
|
|
717
|
+
return unless match = address_regexp.match(address)
|
|
715
718
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
parses only an address and returns an instance of
|
|
719
|
-
StreetAddress::US::Address or nil if the address cannot be parsed
|
|
720
|
-
|
|
721
|
-
====example
|
|
722
|
-
address = StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
|
|
723
|
-
assert !address.intersection?
|
|
724
|
-
|
|
725
|
-
=end
|
|
726
|
-
def parse_address(addr)
|
|
727
|
-
regex = Regexp.new(address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
|
|
728
|
-
|
|
729
|
-
return unless match = regex.match(addr)
|
|
730
|
-
|
|
731
|
-
normalize_address(
|
|
732
|
-
StreetAddress::US::Address.new(
|
|
733
|
-
:number => match[1],
|
|
734
|
-
:street => match[5] || match[10] || match[2],
|
|
735
|
-
:street_type => match[6] || match[3],
|
|
736
|
-
:unit => match[15],
|
|
737
|
-
:unit_prefix => match[13] || match[14],
|
|
738
|
-
:suffix => match[7] || match[12],
|
|
739
|
-
:prefix => match[4],
|
|
740
|
-
:city => match[16],
|
|
741
|
-
:state => match[17],
|
|
742
|
-
:postal_code => match[18],
|
|
743
|
-
:postal_code_ext => match[19]
|
|
744
|
-
)
|
|
745
|
-
)
|
|
719
|
+
to_address( match_to_hash(match), args )
|
|
746
720
|
end
|
|
747
721
|
|
|
748
|
-
def parse_informal_address(
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
return unless match = regex.match(addr)
|
|
752
|
-
|
|
753
|
-
normalize_address(
|
|
754
|
-
StreetAddress::US::Address.new(
|
|
755
|
-
:number => match[4],
|
|
756
|
-
:street => match[8] || match[13] || match[5],
|
|
757
|
-
:street_type => match[9] || match[6],
|
|
758
|
-
:unit => match[18] || match[3],
|
|
759
|
-
:unit_prefix => match[16] || match[17] || match[1] || match[2],
|
|
760
|
-
:suffix => match[10] || match[15],
|
|
761
|
-
:prefix => match[7],
|
|
762
|
-
:city => match[19],
|
|
763
|
-
:state => match[20],
|
|
764
|
-
:postal_code => match[21],
|
|
765
|
-
:postal_code_ext => match[22]
|
|
766
|
-
)
|
|
767
|
-
)
|
|
768
|
-
end
|
|
722
|
+
def parse_informal_address(address, args={})
|
|
723
|
+
return unless match = informal_address_regexp.match(address)
|
|
769
724
|
|
|
770
|
-
|
|
771
|
-
def normalize_address(addr)
|
|
772
|
-
addr.state = normalize_state(addr.state) unless addr.state.nil?
|
|
773
|
-
addr.street_type = normalize_street_type(addr.street_type) unless addr.street_type.nil?
|
|
774
|
-
addr.prefix = normalize_directional(addr.prefix) unless addr.prefix.nil?
|
|
775
|
-
addr.suffix = normalize_directional(addr.suffix) unless addr.suffix.nil?
|
|
776
|
-
addr.street.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street.nil?
|
|
777
|
-
addr.street_type2 = normalize_street_type(addr.street_type2) unless addr.street_type2.nil?
|
|
778
|
-
addr.prefix2 = normalize_directional(addr.prefix2) unless addr.prefix2.nil?
|
|
779
|
-
addr.suffix2 = normalize_directional(addr.suffix2) unless addr.suffix2.nil?
|
|
780
|
-
addr.street2.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street2.nil?
|
|
781
|
-
addr.city.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.city.nil?
|
|
782
|
-
addr.unit_prefix.capitalize! unless addr.unit_prefix.nil?
|
|
783
|
-
return addr
|
|
725
|
+
to_address( match_to_hash(match), args )
|
|
784
726
|
end
|
|
785
727
|
|
|
786
|
-
def
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
728
|
+
def parse_intersection(intersection, args)
|
|
729
|
+
return unless match = intersection_regexp.match(intersection)
|
|
730
|
+
|
|
731
|
+
hash = match_to_hash(match)
|
|
732
|
+
|
|
733
|
+
streets = intersection_regexp.named_captures["street"].map { |pos|
|
|
734
|
+
match[pos.to_i]
|
|
735
|
+
}.select { |v| v }
|
|
736
|
+
hash["street"] = streets[0] if streets[0]
|
|
737
|
+
hash["street2"] = streets[1] if streets[1]
|
|
738
|
+
|
|
739
|
+
street_types = intersection_regexp.named_captures["street_type"].map { |pos|
|
|
740
|
+
match[pos.to_i]
|
|
741
|
+
}.select { |v| v }
|
|
742
|
+
hash["street_type"] = street_types[0] if street_types[0]
|
|
743
|
+
hash["street_type2"] = street_types[1] if street_types[1]
|
|
744
|
+
|
|
745
|
+
if(
|
|
746
|
+
hash["street_type"] &&
|
|
747
|
+
(
|
|
748
|
+
!hash["street_type2"] ||
|
|
749
|
+
(hash["street_type"] == hash["street_type2"])
|
|
750
|
+
)
|
|
751
|
+
)
|
|
752
|
+
type = hash["street_type"].clone
|
|
753
|
+
if( type.gsub!(/s\W*$/i, '') && /\A#{street_type_regexp}\z/i =~ type )
|
|
754
|
+
hash["street_type"] = hash["street_type2"] = type
|
|
755
|
+
end
|
|
791
756
|
end
|
|
792
|
-
end
|
|
793
757
|
|
|
794
|
-
|
|
795
|
-
s_type.downcase!
|
|
796
|
-
s_type = STREET_TYPES[s_type] || s_type if STREET_TYPES_LIST[s_type]
|
|
797
|
-
s_type.capitalize
|
|
758
|
+
to_address( hash, args )
|
|
798
759
|
end
|
|
799
760
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
761
|
+
private
|
|
762
|
+
def match_to_hash(match)
|
|
763
|
+
hash = {}
|
|
764
|
+
match.names.each { |name| hash[name] = match[name] if match[name] }
|
|
765
|
+
return hash
|
|
805
766
|
end
|
|
806
|
-
end
|
|
807
|
-
end
|
|
808
767
|
|
|
809
|
-
|
|
768
|
+
def to_address(input, args)
|
|
769
|
+
# strip off some punctuation and whitespace
|
|
770
|
+
input.values.each { |string|
|
|
771
|
+
string.strip!
|
|
772
|
+
string.gsub!(/[^\w\s\-\#\&]/, '')
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
input['redundant_street_type'] = false
|
|
776
|
+
if( input['street'] && !input['street_type'] )
|
|
777
|
+
match = street_regexp.match(input['street'])
|
|
778
|
+
input['street_type'] = match['street_type']
|
|
779
|
+
input['redundant_street_type'] = true
|
|
780
|
+
end
|
|
810
781
|
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
782
|
+
NORMALIZE_MAP.each_pair { |key, map|
|
|
783
|
+
next unless input[key]
|
|
784
|
+
mapping = map[input[key].downcase]
|
|
785
|
+
input[key] = mapping if mapping
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
if( args[:avoid_redundant_street_type] )
|
|
789
|
+
['', '1', '2'].each { |suffix|
|
|
790
|
+
street = input['street' + suffix];
|
|
791
|
+
type = input['street_type' + suffix];
|
|
792
|
+
next if !street || !type
|
|
793
|
+
|
|
794
|
+
type_regexp = street_type_matches[type.downcase] # || fail "No STREET_TYPE_MATCH for #{type}"
|
|
795
|
+
input.delete('street_type' + suffix) if type_regexp.match(street)
|
|
796
|
+
}
|
|
797
|
+
end
|
|
798
|
+
|
|
799
|
+
# attempt to expand directional prefixes on place names
|
|
800
|
+
if( input['city'] )
|
|
801
|
+
input['city'].gsub!(/^(#{dircode_regexp})\s+(?=\S)/) { |match|
|
|
802
|
+
DIRECTION_CODES[match[0].upcase] + ' '
|
|
803
|
+
}
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
%w(street street_type street2 street_type2 city unit_prefix).each do |k|
|
|
807
|
+
input[k] = input[k].split.map(&:capitalize).join(' ') if input[k]
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
return StreetAddress::US::Address.new( input )
|
|
811
|
+
end
|
|
812
|
+
end
|
|
814
813
|
|
|
815
|
-
=end
|
|
816
814
|
class Address
|
|
817
815
|
attr_accessor(
|
|
818
816
|
:number,
|
|
@@ -829,84 +827,101 @@ module StreetAddress
|
|
|
829
827
|
:street2,
|
|
830
828
|
:street_type2,
|
|
831
829
|
:suffix2,
|
|
832
|
-
:prefix2
|
|
830
|
+
:prefix2,
|
|
831
|
+
:redundant_street_type
|
|
833
832
|
)
|
|
834
833
|
|
|
835
834
|
def initialize(args)
|
|
836
|
-
args.
|
|
837
|
-
|
|
835
|
+
args.each do |attr, val|
|
|
836
|
+
public_send("#{attr}=", val)
|
|
838
837
|
end
|
|
839
|
-
return
|
|
840
838
|
end
|
|
841
839
|
|
|
840
|
+
|
|
841
|
+
def full_postal_code
|
|
842
|
+
return nil unless self.postal_code
|
|
843
|
+
self.postal_code_ext ? "#{postal_code}-#{postal_code_ext}" : self.postal_code
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
|
|
842
847
|
def state_fips
|
|
843
848
|
StreetAddress::US::FIPS_STATES[state]
|
|
844
849
|
end
|
|
845
850
|
|
|
851
|
+
|
|
846
852
|
def state_name
|
|
847
|
-
name = StreetAddress::US::STATE_NAMES[state]
|
|
853
|
+
name = StreetAddress::US::STATE_NAMES[state]
|
|
854
|
+
name&.split&.map(&:capitalize)&.join(" ")
|
|
848
855
|
end
|
|
849
856
|
|
|
857
|
+
|
|
850
858
|
def intersection?
|
|
851
859
|
!street2.nil?
|
|
852
860
|
end
|
|
853
861
|
|
|
854
|
-
|
|
862
|
+
|
|
863
|
+
def line1
|
|
864
|
+
parts = []
|
|
855
865
|
if intersection?
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
866
|
+
parts << prefix if prefix
|
|
867
|
+
parts << street
|
|
868
|
+
parts << street_type if street_type
|
|
869
|
+
parts << suffix if suffix
|
|
870
|
+
parts << "and"
|
|
871
|
+
parts << prefix2 if prefix2
|
|
872
|
+
parts << street2
|
|
873
|
+
parts << street_type2 if street_type2
|
|
874
|
+
parts << suffix2 if suffix2
|
|
865
875
|
else
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
876
|
+
parts << number
|
|
877
|
+
parts << prefix if prefix
|
|
878
|
+
parts << street if street
|
|
879
|
+
parts << street_type if street_type && !redundant_street_type
|
|
880
|
+
parts << suffix if suffix
|
|
881
|
+
parts << unit_prefix if unit_prefix
|
|
882
|
+
# follow USPS guidelines: http://pe.usps.gov/cpim/ftp/pubs/Pub28/pub28.pdf pg28
|
|
883
|
+
parts << (unit_prefix ? unit : "# #{unit}") if unit
|
|
884
|
+
end
|
|
885
|
+
parts.join(" ").strip
|
|
886
|
+
end
|
|
887
|
+
|
|
877
888
|
|
|
878
|
-
|
|
889
|
+
def line2
|
|
890
|
+
parts = []
|
|
891
|
+
parts << city if city
|
|
892
|
+
parts << state if state
|
|
893
|
+
s = parts.join(", ")
|
|
894
|
+
if postal_code
|
|
895
|
+
s = "#{s} #{postal_code}"
|
|
896
|
+
s = "#{s}-#{postal_code_ext}" if postal_code_ext
|
|
879
897
|
end
|
|
898
|
+
s.strip
|
|
880
899
|
end
|
|
881
900
|
|
|
901
|
+
|
|
882
902
|
def to_s(format = :default)
|
|
883
|
-
s = ""
|
|
884
903
|
case format
|
|
885
904
|
when :line1
|
|
886
|
-
|
|
905
|
+
line1
|
|
906
|
+
when :line2
|
|
907
|
+
line2
|
|
887
908
|
else
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
s += ", " + city unless city.nil?
|
|
899
|
-
s += ", " + state unless state.nil?
|
|
900
|
-
s += " " + postal_code unless postal_code.nil?
|
|
901
|
-
else
|
|
902
|
-
s += line1(s)
|
|
903
|
-
s += ", " + city unless city.nil?
|
|
904
|
-
s += ", " + state unless state.nil?
|
|
905
|
-
s += " " + postal_code unless postal_code.nil?
|
|
906
|
-
s += "-" + postal_code_ext unless postal_code_ext.nil?
|
|
907
|
-
end
|
|
909
|
+
[line1, line2].reject(&:empty?).join(", ")
|
|
910
|
+
end
|
|
911
|
+
end
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
def to_h
|
|
915
|
+
self.instance_variables.each_with_object({}) do |var_name, hash|
|
|
916
|
+
var_value = self.instance_variable_get(var_name)
|
|
917
|
+
hash_name = var_name[1..-1].to_sym
|
|
918
|
+
hash[hash_name] = var_value
|
|
908
919
|
end
|
|
909
|
-
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def ==(other)
|
|
923
|
+
return false if other.nil?
|
|
924
|
+
to_s == other.to_s
|
|
910
925
|
end
|
|
911
926
|
end
|
|
912
927
|
end
|