StreetAddress 1.0.5 → 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 +7 -0
- data/CHANGELOG.md +48 -0
- data/LICENCE +21 -0
- data/README.md +103 -0
- data/lib/street_address.rb +736 -707
- metadata +103 -53
- data/LICENSE +0 -22
- data/README.rdoc +0 -45
- data/Rakefile +0 -8
- data/test/test_street_address.rb +0 -175
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,378 +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
|
-
STREET_TYPES_LIST =
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
|
449
391
|
end
|
|
450
392
|
|
|
451
393
|
STATE_CODES = {
|
|
@@ -508,10 +450,10 @@ module StreetAddress
|
|
|
508
450
|
"west virginia" => "WV",
|
|
509
451
|
"wisconsin" => "WI",
|
|
510
452
|
"wyoming" => "WY"
|
|
511
|
-
}
|
|
453
|
+
}.freeze
|
|
454
|
+
|
|
455
|
+
STATE_NAMES = STATE_CODES.invert.freeze
|
|
512
456
|
|
|
513
|
-
STATE_NAMES = STATE_CODES.invert
|
|
514
|
-
|
|
515
457
|
STATE_FIPS = {
|
|
516
458
|
"01" => "AL",
|
|
517
459
|
"02" => "AK",
|
|
@@ -566,334 +508,421 @@ module StreetAddress
|
|
|
566
508
|
"56" => "WY",
|
|
567
509
|
"72" => "PR",
|
|
568
510
|
"78" => "VI"
|
|
569
|
-
}
|
|
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
|
|
570
527
|
|
|
571
|
-
FIPS_STATES = STATE_FIPS.invert
|
|
572
|
-
|
|
573
528
|
class << self
|
|
574
529
|
attr_accessor(
|
|
575
530
|
:street_type_regexp,
|
|
531
|
+
:street_type_matches,
|
|
576
532
|
:number_regexp,
|
|
577
533
|
:fraction_regexp,
|
|
578
534
|
:state_regexp,
|
|
579
535
|
:city_and_state_regexp,
|
|
580
|
-
:direct_regexp,
|
|
536
|
+
:direct_regexp,
|
|
581
537
|
:zip_regexp,
|
|
582
538
|
:corner_regexp,
|
|
583
|
-
:unit_regexp,
|
|
584
539
|
:street_regexp,
|
|
585
540
|
:place_regexp,
|
|
586
541
|
:address_regexp,
|
|
587
|
-
: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
|
|
588
550
|
)
|
|
589
551
|
end
|
|
590
|
-
|
|
591
|
-
self.
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
552
|
+
|
|
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 = /
|
|
596
587
|
(?:
|
|
597
|
-
|
|
598
|
-
(
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
f = x.gsub(/(\w)/, '\1.')
|
|
607
|
-
[Regexp::quote(f), Regexp::quote(x)]
|
|
608
|
-
}.join("|")
|
|
609
|
-
self.zip_regexp = '(\d{5})(?:-?(\d{4})?)'
|
|
610
|
-
self.corner_regexp = '(?:\band\b|\bat\b|&|\@)'
|
|
611
|
-
self.unit_regexp = '(?:(su?i?te|p\W*[om]\W*b(?:ox)?|dept|apt|apartment|ro*m|fl|unit|box)\W+|\#\W*)([\w-]+)'
|
|
612
|
-
self.street_regexp =
|
|
613
|
-
'(?:
|
|
614
|
-
(?:(' + direct_regexp + ')\W+
|
|
615
|
-
(' + 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)
|
|
616
597
|
|
|
|
617
|
-
(
|
|
618
|
-
(?:
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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*
|
|
629
649
|
)
|
|
630
|
-
|
|
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
|
-
|
|
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
|
+
|
|
667
707
|
class << self
|
|
668
|
-
def parse(location, args
|
|
669
|
-
if
|
|
670
|
-
parse_intersection(location)
|
|
671
|
-
|
|
672
|
-
parse_address(location) || parse_informal_address(location)
|
|
673
|
-
else
|
|
674
|
-
parse_address(location);
|
|
708
|
+
def parse(location, args={})
|
|
709
|
+
if( corner_regexp.match(location) )
|
|
710
|
+
return parse_intersection(location, args)
|
|
711
|
+
else
|
|
712
|
+
return parse_address(location, args) || parse_informal_address(location, args)
|
|
675
713
|
end
|
|
676
714
|
end
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
====example
|
|
683
|
-
address = StreetAddress::US.parse('Hollywood & Vine, Los Angeles, CA')
|
|
684
|
-
assert address.intersection?
|
|
685
|
-
|
|
686
|
-
=end
|
|
687
|
-
def parse_intersection(inter)
|
|
688
|
-
regex = Regexp.new(
|
|
689
|
-
'\A\W*' + street_regexp + '\W*?
|
|
690
|
-
\s+' + corner_regexp + '\s+' +
|
|
691
|
-
street_regexp + '\W+' +
|
|
692
|
-
place_regexp + '\W*\Z', Regexp::IGNORECASE + Regexp::EXTENDED
|
|
693
|
-
)
|
|
694
|
-
|
|
695
|
-
return unless match = regex.match(inter)
|
|
696
|
-
|
|
697
|
-
normalize_address(
|
|
698
|
-
StreetAddress::US::Address.new(
|
|
699
|
-
:street => match[4] || match[9],
|
|
700
|
-
:street_type => match[5],
|
|
701
|
-
:suffix => match[6],
|
|
702
|
-
:prefix => match[3],
|
|
703
|
-
:street2 => match[15] || match[20],
|
|
704
|
-
:street_type2 => match[16],
|
|
705
|
-
:suffix2 => match[17],
|
|
706
|
-
:prefix2 => match[14],
|
|
707
|
-
:city => match[23],
|
|
708
|
-
:state => match[24],
|
|
709
|
-
:postal_code => match[25]
|
|
710
|
-
)
|
|
711
|
-
)
|
|
715
|
+
|
|
716
|
+
def parse_address(address, args={})
|
|
717
|
+
return unless match = address_regexp.match(address)
|
|
718
|
+
|
|
719
|
+
to_address( match_to_hash(match), args )
|
|
712
720
|
end
|
|
713
|
-
|
|
714
|
-
=
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
====example
|
|
720
|
-
address = StreetAddress::US.parse('1600 Pennsylvania Ave Washington, DC 20006')
|
|
721
|
-
assert !address.intersection?
|
|
722
|
-
|
|
723
|
-
=end
|
|
724
|
-
def parse_address(addr)
|
|
725
|
-
regex = Regexp.new(address_regexp, Regexp::IGNORECASE + Regexp::EXTENDED)
|
|
726
|
-
|
|
727
|
-
return unless match = regex.match(addr)
|
|
728
|
-
|
|
729
|
-
normalize_address(
|
|
730
|
-
StreetAddress::US::Address.new(
|
|
731
|
-
:number => match[1],
|
|
732
|
-
:street => match[5] || match[10] || match[2],
|
|
733
|
-
:street_type => match[6] || match[3],
|
|
734
|
-
:unit => match[14],
|
|
735
|
-
:unit_prefix => match[13],
|
|
736
|
-
:suffix => match[7] || match[12],
|
|
737
|
-
:prefix => match[4],
|
|
738
|
-
:city => match[15],
|
|
739
|
-
:state => match[16],
|
|
740
|
-
:postal_code => match[17],
|
|
741
|
-
:postal_code_ext => match[18]
|
|
742
|
-
)
|
|
743
|
-
)
|
|
721
|
+
|
|
722
|
+
def parse_informal_address(address, args={})
|
|
723
|
+
return unless match = informal_address_regexp.match(address)
|
|
724
|
+
|
|
725
|
+
to_address( match_to_hash(match), args )
|
|
744
726
|
end
|
|
745
727
|
|
|
746
|
-
def
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
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
|
+
)
|
|
765
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
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
to_address( hash, args )
|
|
766
759
|
end
|
|
767
|
-
|
|
760
|
+
|
|
768
761
|
private
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
addr.suffix = normalize_directional(addr.suffix) unless addr.suffix.nil?
|
|
774
|
-
addr.street.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street.nil?
|
|
775
|
-
addr.street_type2 = normalize_street_type(addr.street_type2) unless addr.street_type2.nil?
|
|
776
|
-
addr.prefix2 = normalize_directional(addr.prefix2) unless addr.prefix2.nil?
|
|
777
|
-
addr.suffix2 = normalize_directional(addr.suffix2) unless addr.suffix2.nil?
|
|
778
|
-
addr.street2.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.street2.nil?
|
|
779
|
-
addr.city.gsub!(/\b([a-z])/) {|wd| wd.capitalize} unless addr.city.nil?
|
|
780
|
-
addr.unit_prefix.capitalize! unless addr.unit_prefix.nil?
|
|
781
|
-
return addr
|
|
782
|
-
end
|
|
783
|
-
|
|
784
|
-
def normalize_state(state)
|
|
785
|
-
if state.length < 3
|
|
786
|
-
state.upcase
|
|
787
|
-
else
|
|
788
|
-
STATE_CODES[state.downcase]
|
|
762
|
+
def match_to_hash(match)
|
|
763
|
+
hash = {}
|
|
764
|
+
match.names.each { |name| hash[name] = match[name] if match[name] }
|
|
765
|
+
return hash
|
|
789
766
|
end
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
767
|
+
|
|
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
|
|
781
|
+
|
|
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 )
|
|
803
811
|
end
|
|
804
|
-
end
|
|
805
812
|
end
|
|
806
813
|
|
|
807
|
-
=begin rdoc
|
|
808
|
-
|
|
809
|
-
This is class returned by StreetAddress::US::parse, StreetAddress::US::parse_address
|
|
810
|
-
and StreetAddress::US::parse_intersection. If an instance represents an intersection
|
|
811
|
-
the attribute street2 will be populated.
|
|
812
|
-
|
|
813
|
-
=end
|
|
814
814
|
class Address
|
|
815
815
|
attr_accessor(
|
|
816
|
-
:number,
|
|
817
|
-
:street,
|
|
818
|
-
:street_type,
|
|
819
|
-
:unit,
|
|
820
|
-
:unit_prefix,
|
|
821
|
-
:suffix,
|
|
822
|
-
:prefix,
|
|
823
|
-
:city,
|
|
824
|
-
:state,
|
|
825
|
-
:postal_code,
|
|
826
|
-
:postal_code_ext,
|
|
827
|
-
:street2,
|
|
828
|
-
:street_type2,
|
|
829
|
-
:suffix2,
|
|
830
|
-
:prefix2
|
|
816
|
+
:number,
|
|
817
|
+
:street,
|
|
818
|
+
:street_type,
|
|
819
|
+
:unit,
|
|
820
|
+
:unit_prefix,
|
|
821
|
+
:suffix,
|
|
822
|
+
:prefix,
|
|
823
|
+
:city,
|
|
824
|
+
:state,
|
|
825
|
+
:postal_code,
|
|
826
|
+
:postal_code_ext,
|
|
827
|
+
:street2,
|
|
828
|
+
:street_type2,
|
|
829
|
+
:suffix2,
|
|
830
|
+
:prefix2,
|
|
831
|
+
:redundant_street_type
|
|
831
832
|
)
|
|
832
833
|
|
|
833
834
|
def initialize(args)
|
|
834
|
-
args.
|
|
835
|
-
|
|
835
|
+
args.each do |attr, val|
|
|
836
|
+
public_send("#{attr}=", val)
|
|
836
837
|
end
|
|
837
|
-
return
|
|
838
838
|
end
|
|
839
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
|
+
|
|
840
847
|
def state_fips
|
|
841
848
|
StreetAddress::US::FIPS_STATES[state]
|
|
842
849
|
end
|
|
843
850
|
|
|
851
|
+
|
|
844
852
|
def state_name
|
|
845
|
-
name = StreetAddress::US::STATE_NAMES[state]
|
|
853
|
+
name = StreetAddress::US::STATE_NAMES[state]
|
|
854
|
+
name&.split&.map(&:capitalize)&.join(" ")
|
|
846
855
|
end
|
|
847
856
|
|
|
857
|
+
|
|
848
858
|
def intersection?
|
|
849
859
|
!street2.nil?
|
|
850
860
|
end
|
|
851
861
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
862
|
+
|
|
863
|
+
def line1
|
|
864
|
+
parts = []
|
|
865
|
+
if intersection?
|
|
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
|
|
875
|
+
else
|
|
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
|
+
|
|
888
|
+
|
|
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
|
|
863
897
|
end
|
|
864
|
-
s
|
|
865
|
-
return s
|
|
898
|
+
s.strip
|
|
866
899
|
end
|
|
867
900
|
|
|
901
|
+
|
|
868
902
|
def to_s(format = :default)
|
|
869
|
-
s = ""
|
|
870
903
|
case format
|
|
871
904
|
when :line1
|
|
872
|
-
|
|
905
|
+
line1
|
|
906
|
+
when :line2
|
|
907
|
+
line2
|
|
873
908
|
else
|
|
874
|
-
|
|
875
|
-
s += prefix + " " unless prefix.nil?
|
|
876
|
-
s += street
|
|
877
|
-
s += " " + street_type unless street_type.nil?
|
|
878
|
-
s += " " + suffix unless suffix.nil?
|
|
879
|
-
s += " and"
|
|
880
|
-
s += " " + prefix2 unless prefix2.nil?
|
|
881
|
-
s += " " + street2
|
|
882
|
-
s += " " + street_type2 unless street_type2.nil?
|
|
883
|
-
s += " " + suffix2 unless suffix2.nil?
|
|
884
|
-
s += ", " + city unless city.nil?
|
|
885
|
-
s += ", " + state unless state.nil?
|
|
886
|
-
s += " " + postal_code unless postal_code.nil?
|
|
887
|
-
else
|
|
888
|
-
s += line1(s)
|
|
889
|
-
s += ", " + city unless city.nil?
|
|
890
|
-
s += ", " + state unless state.nil?
|
|
891
|
-
s += " " + postal_code unless postal_code.nil?
|
|
892
|
-
s += "-" + postal_code_ext unless postal_code_ext.nil?
|
|
893
|
-
end
|
|
909
|
+
[line1, line2].reject(&:empty?).join(", ")
|
|
894
910
|
end
|
|
895
|
-
|
|
896
|
-
|
|
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
|
|
919
|
+
end
|
|
920
|
+
end
|
|
921
|
+
|
|
922
|
+
def ==(other)
|
|
923
|
+
return false if other.nil?
|
|
924
|
+
to_s == other.to_s
|
|
925
|
+
end
|
|
897
926
|
end
|
|
898
927
|
end
|
|
899
928
|
end
|