census_shapes 0.2.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.
@@ -0,0 +1,4 @@
1
+ CENSUS_HOST = 'ftp.census.gov'
2
+ CENSUS_SHAPES_PATH = 'geo/tiger/TIGER2010'
3
+ CENSUS_STATES = YAML.load_file('lib/yaml/us_states.yml')
4
+ CENSUS_SHAPES = YAML.load_file('lib/yaml/us_shapes.yml')
@@ -0,0 +1,17 @@
1
+ class CONTROLLER_NAMEController < ApplicationController
2
+
3
+ def index
4
+ bbox = MODEL_NAME.bbox(params[:z],params[:x], params[:y])
5
+ params.merge!(:bbox=> bbox) if bbox
6
+ respond_to do |format|
7
+ format.html
8
+ format.json {
9
+ render json: ({
10
+ :status=> "OK",
11
+ :type => "FeatureCollection",
12
+ :features=> MODEL_NAME.features(params)
13
+ })
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,86 @@
1
+ class Create<%= controller_name.titleize.gsub(' ', '').gsub(/\W/,'') %> < ActiveRecord::Migration
2
+ def up
3
+ create_table(:<%= controller_name %>) do |t|
4
+ t.string :type, :limit => 12
5
+ t.string :sumlevel, :limit => 3
6
+ t.string :geoid10, :limit => 15
7
+ t.string :name10, :limit => 100
8
+ t.string :namelsad10, :limit => 100
9
+ t.string :region10, :limit => 2
10
+ t.string :division10, :limit => 2
11
+ t.string :state10, :limit => 2
12
+ t.string :statens10, :limit => 8
13
+ t.string :statefp10, :limit => 2
14
+ t.string :zcta5ce10, :limit => 5
15
+ t.string :countyfp10, :limit => 3
16
+ t.string :countyns10, :limit => 8
17
+ t.string :cousubfp10, :limit => 5
18
+ t.string :cousubns10, :limit => 8
19
+ t.string :submcdfp10, :limit => 5
20
+ t.string :submcdns10, :limit => 8
21
+ t.string :tractce10, :limit => 6
22
+ t.string :blockce10, :limit => 4
23
+ t.string :blkgrpce10, :limit => 6
24
+ t.string :placefp10, :limit => 5
25
+ t.string :placens10, :limit => 8
26
+ t.string :csafp10, :limit => 3
27
+ t.string :cbsafp10, :limit => 5
28
+ t.string :metdivfp10, :limit => 5
29
+ t.string :cd111fp, :limit => 2
30
+ t.string :cdsessn, :limit => 3
31
+ t.string :anrcfp10, :limit => 5
32
+ t.string :anrcns10, :limit => 8
33
+ t.string :aiannhce10, :limit => 4
34
+ t.string :aiannhns10, :limit => 8
35
+ t.string :trsubce10, :limit => 3
36
+ t.string :trsubns10, :limit => 8
37
+ t.string :sldust10, :limit => 3
38
+ t.string :sldlst10, :limit => 3
39
+ t.string :vtdst10, :limit => 6
40
+ t.string :elsdlea10, :limit => 5
41
+ t.string :scsdlea10, :limit => 5
42
+ t.string :unsdlea10, :limit => 5
43
+ t.string :stusps10, :limit => 2
44
+ t.string :lsad10, :limit => 2
45
+ t.string :lsy10, :limit=> 4
46
+ t.string :lograde10, :limit => 2
47
+ t.string :higrade10, :limit => 2
48
+ t.string :sdtyp10, :limigt => 1
49
+ t.string :classfp10, :limit => 2
50
+ t.string :comptyp10, :limit => 1
51
+ t.string :aiannhr10, :limit => 1
52
+ t.string :aiannhfp10, :limit => 5
53
+ t.string :trsubfp10, :limit => 5
54
+ t.string :partflg10, :limit => 1
55
+ t.string :pcicbsa10, :limit => 1
56
+ t.string :pcinecta10, :limit => 1
57
+ t.string :mtfcc10, :limit => 5
58
+ t.string :memi10, :limit => 1
59
+ t.string :ur10, :limit => 1
60
+ t.string :uace10, :limit => 1
61
+ t.string :uatyp10, :limit => 1
62
+ t.string :vtdi10, :limit => 1
63
+ t.string :cnectafp10, :limit => 3
64
+ t.string :nectafp10, :limit => 5
65
+ t.string :nctadvfp10, :limit => 5
66
+ t.string :funcstat10, :limit => 1
67
+ t.float :aland10, :length => 8
68
+ t.float :awater10, :length => 8
69
+ t.point :latlng, :srid=> 4326
70
+ t.geometry :geom, :srid=> 4326
71
+ t.decimal :intptlat10, :precision => 15, :scale => 12
72
+ t.decimal :intptlon10, :precision => 15, :scale => 12
73
+ end
74
+ add_index :<%= controller_name %>, [:type,:geoid10], :name => "geo_index", :unique=> true
75
+ execute "ALTER TABLE <%= controller_name %> RENAME COLUMN id TO gid;"
76
+ execute "CREATE INDEX b_point ON <%= controller_name %> USING GIST (latlng);";
77
+ execute "CREATE INDEX b_geom ON <%= controller_name %> USING GIST (geom);";
78
+ end
79
+
80
+ def down
81
+ drop_table :<%= controller_name %>
82
+ end
83
+ end
84
+
85
+
86
+
@@ -0,0 +1,167 @@
1
+ require 'net/ftp'
2
+ require 'yaml'
3
+ require 'progress_bar'
4
+
5
+ CONFIG = YAML::load(File.open('config/database.yml'))
6
+ DB = CONFIG[Rails.env]['database']
7
+ USER = CONFIG[Rails.env]['username']
8
+ HOST = CONFIG[Rails.env]['host']
9
+ TEMPLATE = CONFIG[Rails.env]['template']
10
+
11
+ namespace :census_shapes do
12
+ desc "Import Census Shapes"
13
+ task :import => :environment do
14
+ if local_path(ENV['FILEPATH'])
15
+ for geo in format_args(ENV['SHAPE'], msg('install'))
16
+ before_import_set_defaults(geo)
17
+ puts "Importing Census Shape #{geo['name']} (#{geo['slug']})"
18
+ @dbar = ProgressBar.new(52)
19
+ for state in CENSUS_STATES
20
+ path = "#{CENSUS_SHAPES_PATH}/#{geo['path']}/"
21
+ file = "tl_2010_#{state[1]["id"].to_s.rjust(2,'0')}_#{geo['slug'].downcase}10"
22
+ download_zip(path, file, geo['slug'])
23
+ unzip_shapefiles(file, geo['slug'])
24
+ import_shapefiles(file, geo['slug'], ENV['ARCHIVE'])
25
+ @dbar.increment!
26
+ end
27
+ after_import_update_fields
28
+ end
29
+ end
30
+ end
31
+
32
+ desc "Validate Geometry"
33
+ task :validate_geometry => :environment do
34
+ @dbar = ProgressBar.new(Geography.count)
35
+ Geography.find_in_batches(:select=> "gid, geoid10, type, ST_IsValid(geom) AS isvalid", :batch_size => 100) do |batch|
36
+ batch.each do |geo|
37
+ if geo['isvalid'] == 'f';
38
+ puts "Geography #{geo['type']} : gid = #{geo['gid']}, geoid10 = #{geo['geoid10']} is #{geo['isvalid']}"
39
+ ActiveRecord::Base.connection.execute("UPDATE CONTROLLER_NAME SET geom = '#{geo.fix_geometry['geo']}' WHERE geoid10 = '#{geo.geoid10}' AND type = '#{geo.type}'")
40
+ puts "Geography fixed? #{ActiveRecord::Base.connection.execute("SELECT ST_IsValid(geom) AS isvalid FROM Geographies WHERE geoid10 = '#{geo.geoid10}' AND type = '#{geo.type}'").first['isvalid'] == 't'}"
41
+ end
42
+ @dbar.increment!
43
+ end
44
+ end
45
+ end
46
+
47
+ desc "Repair Geometry"
48
+ task :repair_geometry => :environment do
49
+ for geo in format_args(ENV['SHAPE'], msg('repair'))
50
+ @dbar = ProgressBar.new(Geography.where(:type=> geo['slug'].capitalize).count)
51
+ Geography.find_in_batches(:select=> "gid, geoid10, name10, type, latlng, ST_AsText(geom) AS geo", :conditions => {:type => geo['slug'].capitalize}, :batch_size => 100) do |batch|
52
+ batch.each do |b|
53
+ rebuild = false
54
+ ids = ActiveRecord::Base.connection.execute("SELECT c.geoid10 FROM CONTROLLER_NAME AS p, CONTROLLER_NAME AS c WHERE c.geoid10 != '#{b.geoid10}' AND p.geoid10 = '#{b.geoid10}' AND c.type='#{b.type}' AND ST_DWithin(p.latlng, c.latlng, .5);").map{|id| id['geoid10']}
55
+ if !ids.empty?
56
+ intersects = Geography.where("geoid10 IN ('#{ids.join("','")}') AND type = '#{b.type}' AND ST_Intersects('#{b['geo']}', geom)")
57
+ if !intersects.empty?
58
+ polygons = b.polygons
59
+ intersects.each do |int|
60
+ int.polygons.each_with_index do |c, i|
61
+ current = ActiveRecord::Base.connection.execute("SELECT ST_Within('#{c}','#{b['geo']}') AS within").first
62
+ if !polygons.include?(c) && current['within'] == "t"
63
+ match = ActiveRecord::Base.connection.execute("SELECT #{polygons.map.with_index{|p, i| "ST_Intersects('#{p}', '#{c}') AS poly_#{i}"}.join(",")}").first.values.map{|aa| aa == "t" }
64
+ if !match.include?(true)
65
+ polygons << c
66
+ rebuild = true
67
+ puts "#{b.type} #{b.geoid10} has children #{int.geoid10}"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ if rebuild
73
+ new_geom = Geography.merge(polygons)
74
+ if new_geom['isvalid']
75
+ ActiveRecord::Base.connection.execute("UPDATE CONTROLLER_NAME SET geom = '#{new_geom['geom']}' WHERE geoid10 = '#{b.geoid10}' AND type = '#{b.type}'")
76
+ else
77
+ ActiveRecord::Base.connection.execute("UPDATE CONTROLLER_NAME SET geom = ST_Buffer('#{new_geom['geom']}', .0000001) WHERE geoid10 = '#{b.geoid10}' AND type = '#{b.type}'")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ @dbar.increment!
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def msg(type)
89
+ msg = "\nPlease specify one or more of the following Census shapes:\n\n"
90
+ msg += "#{CENSUS_SHAPES.map{|g| g[0]}.join(",")}\n\n"
91
+ if type == "install"
92
+ msg += "For example: rake census_shapes:install SHAPE=STATE,COUNTY\n\n"
93
+ msg += "Add the option 'ARCHIVE=true' to archive the zip files after importing."
94
+ elsif type== "validate"
95
+ msg += "For example: rake census_shapes:validate_geometry SHAPE=STATE,COUNTY\n\n"
96
+ elsif type== "repair"
97
+ msg += "For example: rake census_shapes:repair_geometry SHAPE=STATE,COUNTY\n\n"
98
+ end
99
+ return msg
100
+ end
101
+
102
+ def format_args(args, msg)
103
+ geos = []
104
+ if args.nil?
105
+ puts msg
106
+ else
107
+ args = args.split(",")
108
+ for arg in args
109
+ g = get_levels(arg.upcase)
110
+ geos << g if g
111
+ end
112
+ end
113
+ return geos
114
+ end
115
+
116
+ def get_levels(level)
117
+ if !level.nil? && CENSUS_SHAPES.include?(level.upcase)
118
+ return CENSUS_SHAPES[level]
119
+ else
120
+ return false
121
+ end
122
+ end
123
+
124
+ def local_path(local="tmp/shapefiles/")
125
+ return @local if @local
126
+ `mkdir -p #{local}`
127
+ if !File.exists?(local) && !File.directory?(local)
128
+ puts "'#{local}' is not a valid file path."
129
+ return false
130
+ end
131
+ @local = local
132
+ return @local
133
+ end
134
+
135
+ def download_zip(path, file, shape)
136
+ if !File.exist?("#{local_path}#{shape}/#{file}.zip")
137
+ `mkdir -p #{local_path}#{shape}`
138
+ ftp = Net::FTP.new(CENSUS_HOST)
139
+ ftp.login(user = "anonymous")
140
+ ftp.chdir(path)
141
+ ftp.getbinaryfile("#{file}.zip", "#{local_path}#{shape}/#{file}.zip")
142
+ ftp.close
143
+ end
144
+ end
145
+
146
+ def self.unzip_shapefiles(file, shape)
147
+ `unzip #{local_path}#{shape}/#{file}.zip -d #{local_path}#{shape}/#{file}` if File.exist?("#{local_path}#{shape}/#{file}.zip")
148
+ end
149
+
150
+ def self.import_shapefiles(file, shape, archive)
151
+ `shp2pgsql -W Latin1 -g geom -a -D #{local_path}#{shape}/#{file}/#{file} CONTROLLER_NAME #{TEMPLATE} | psql -h #{HOST} -U #{USER} -d #{DB}`
152
+ `rm -rf #{local_path}#{shape}/#{file}`
153
+ if archive.nil?
154
+ `rm #{local_path}#{shape}/#{file}.zip`
155
+ end
156
+ end
157
+
158
+ def before_import_set_defaults(geo)
159
+ ActiveRecord::Base.connection.execute("ALTER TABLE ONLY CONTROLLER_NAME ALTER COLUMN type SET DEFAULT '#{geo['slug'].capitalize}'")
160
+ ActiveRecord::Base.connection.execute("ALTER TABLE ONLY CONTROLLER_NAME ALTER COLUMN sumlevel SET DEFAULT '#{geo['sumlevel']}'")
161
+ end
162
+
163
+ def after_import_update_fields
164
+ ActiveRecord::Base.connection.execute("UPDATE CONTROLLER_NAME SET latlng = ST_GeomFromText('POINT(' || intptlon10 || ' ' || intptlat10 || ')', 4326) WHERE latlng IS NULL;")
165
+ ActiveRecord::Base.connection.execute("UPDATE CONTROLLER_NAME SET name10 = namelsad10 WHERE namelsad10 IS NOT NULL AND name10 IS NULL;")
166
+ end
167
+ end
@@ -0,0 +1,22 @@
1
+ namespace :postgis_template do
2
+ desc "Creates Postgis Template"
3
+ task :create do
4
+ config = YAML::load(File.open('config/database.yml'))
5
+ template = config[Rails.env]['template']
6
+ user = config[Rails.env]['username']
7
+ host = config[Rails.env]['host']
8
+ postgis_path = config[Rails.env]['postgis_path']
9
+
10
+ existing = `psql -U #{user} -h #{host} -U #{user} --list`
11
+ if existing.scan(template).empty?
12
+ `createdb -U #{user} #{template} -h #{host} -U #{user} -E UTF8`
13
+ `createlang -d #{template} plpgsql -h #{host} -U #{user}`
14
+ `psql -d postgres -c "UPDATE pg_database SET datistemplate='true' WHERE datname='#{template}';"`
15
+ `psql -h #{host} -U #{user} -d #{template} -f #{postgis_path}postgis.sql`
16
+ `psql -h #{host} -U #{user} -d #{template} -f #{postgis_path}spatial_ref_sys.sql`
17
+ `psql -d #{template} -c "GRANT ALL ON geometry_columns TO PUBLIC;"`
18
+ `psql -d #{template} -c "GRANT ALL ON geography_columns TO PUBLIC;"`
19
+ `psql -d #{template} -c "GRANT ALL ON spatial_ref_sys TO PUBLIC;"`
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,133 @@
1
+ ---
2
+ STATE:
3
+ sumlevel: '040'
4
+ slug: STATE
5
+ name: State
6
+ description: state
7
+ path: STATE/2010
8
+ COUNTY:
9
+ sumlevel: '050'
10
+ slug: COUNTY
11
+ name: County
12
+ description: state-county
13
+ path: COUNTY/2010
14
+ COUSUB:
15
+ sumlevel: '060'
16
+ slug: COUSUB
17
+ name: County Subdivision
18
+ description: state-county-county subdivision
19
+ path: COUSUB/2010
20
+ SUBMCD:
21
+ sumlevel: '067'
22
+ slug: SUBMCD
23
+ name: Subminor Civil Subdivision
24
+ description: state-county-county subdivision-subminor civil subdivision
25
+ path: SUBMCD/2010
26
+ TABBLOCK:
27
+ sumlevel: '101'
28
+ slug: TABBLOCK
29
+ name: Block
30
+ description: state-county-tract-block
31
+ path: TABBLOCK/2010
32
+ TRACT:
33
+ sumlevel: '140'
34
+ slug: TRACT
35
+ name: Tract
36
+ description: state-county-tract
37
+ path: TRACT/2010
38
+ BG:
39
+ sumlevel: '150'
40
+ slug: BG
41
+ name: Block Group
42
+ description: state-county-tract-block group
43
+ path: BG/2010
44
+ PLACE:
45
+ sumlevel: '160'
46
+ slug: PLACE
47
+ name: Place
48
+ description: state-place
49
+ path: PLACE/2010
50
+ ANRC:
51
+ sumlevel: '230'
52
+ slug: ANRC
53
+ name: Alaska Native Regional Corporation
54
+ description: state-alaska native regional corporation
55
+ path: ANRC/2010
56
+ AIANNH:
57
+ sumlevel: '280'
58
+ slug: AIANNH
59
+ name: American Indian Area/Alaska Native Area/Hawaiian Home Land
60
+ description: state-american indian area/alaska native area/hawaiian home land
61
+ path: AIANNH/2010
62
+ AITS:
63
+ sumlevel: '281'
64
+ slug: AITS
65
+ name: American Indian Tribal Subdivision
66
+ description: state-american indian area-tribal subdivision
67
+ path: AITS/2010
68
+ CBSA:
69
+ sumlevel: '320'
70
+ slug: CBSA
71
+ name: Metropolitan Statistical Area/Micropolitan Statistical Area
72
+ description: state-metropolitan statistical area/micropolitan statistical area
73
+ path: CBSA/2010
74
+ METDIV:
75
+ sumlevel: '323'
76
+ slug: METDIV
77
+ name: Metropolitan Division
78
+ description: state-metropolitan statistical area/micropolitan statistical area-metropolitan division
79
+ path: METDIV/2010
80
+ CSA:
81
+ sumlevel: '340'
82
+ slug: CSA
83
+ name: Combined Statistical Area
84
+ description: state-combined statistical area
85
+ path: CSA/2010
86
+ CD:
87
+ sumlevel: '500'
88
+ slug: CD
89
+ name: Congressional District (111th)
90
+ description: state-congressional district (111th)
91
+ path: CD/111
92
+ SLDU:
93
+ sumlevel: '610'
94
+ slug: SLDU
95
+ name: State Legislative District (Upper Chamber)
96
+ description: state-state legislative district (upper chamber)
97
+ path: SLDU/2010
98
+ SLDL:
99
+ sumlevel: '620'
100
+ slug: SLDL
101
+ name: State Legislative District (Lower Chamber)
102
+ description: state-state legislative district (lower chamber)
103
+ path: SLDL/2010
104
+ VTD:
105
+ sumlevel: '700'
106
+ slug: VTD
107
+ name: Voting District
108
+ description: State-County-Voting District/Remainder
109
+ path: VTD/2010
110
+ ZCTA5:
111
+ sumlevel: '871'
112
+ slug: ZCTA5
113
+ name: ZIP Code Tabulation Area
114
+ description: state-zip code tabulation area
115
+ path: ZCTA5/2010
116
+ ELSD:
117
+ sumlevel: '950'
118
+ slug: ELSD
119
+ name: School District (Elementary)
120
+ description: State-School District (Elementary)/Remainder
121
+ path: ELSD/2010
122
+ SCSD:
123
+ sumlevel: '960'
124
+ slug: SCSD
125
+ name: School District (Secondary)
126
+ description: State-School District (Secondary)/Remainder
127
+ path: SCSD/2010
128
+ UNSD:
129
+ sumlevel: '970'
130
+ slug: UNSD
131
+ name: School District (Unified)
132
+ description: State-School District (Unified)/Remainder
133
+ path: UNSD/2010
@@ -0,0 +1,209 @@
1
+ ---
2
+ Alabama:
3
+ id: 1
4
+ abbr: AL
5
+ name: Alabama
6
+ Alaska:
7
+ id: 2
8
+ abbr: AK
9
+ name: Alaska
10
+ Arizona:
11
+ id: 4
12
+ abbr: AZ
13
+ name: Arizona
14
+ Arkansas:
15
+ id: 5
16
+ abbr: AR
17
+ name: Arkansas
18
+ California:
19
+ id: 6
20
+ abbr: CA
21
+ name: California
22
+ Colorado:
23
+ id: 8
24
+ abbr: CO
25
+ name: Colorado
26
+ Connecticut:
27
+ id: 9
28
+ abbr: CT
29
+ name: Connecticut
30
+ Delaware:
31
+ id: 10
32
+ abbr: DE
33
+ name: Delaware
34
+ District of Columbia:
35
+ id: 11
36
+ abbr: DC
37
+ name: District of Columbia
38
+ Florida:
39
+ id: 12
40
+ abbr: FL
41
+ name: Florida
42
+ Georgia:
43
+ id: 13
44
+ abbr: GA
45
+ name: Georgia
46
+ Hawaii:
47
+ id: 15
48
+ abbr: HI
49
+ name: Hawaii
50
+ Idaho:
51
+ id: 16
52
+ abbr: ID
53
+ name: Idaho
54
+ Illinois:
55
+ id: 17
56
+ abbr: IL
57
+ name: Illinois
58
+ Indiana:
59
+ id: 18
60
+ abbr: IN
61
+ name: Indiana
62
+ Iowa:
63
+ id: 19
64
+ abbr: IA
65
+ name: Iowa
66
+ Kansas:
67
+ id: 20
68
+ abbr: KS
69
+ name: Kansas
70
+ Kentucky:
71
+ id: 21
72
+ abbr: KY
73
+ name: Kentucky
74
+ Louisiana:
75
+ id: 22
76
+ abbr: LA
77
+ name: Louisiana
78
+ Maine:
79
+ id: 23
80
+ abbr: ME
81
+ name: Maine
82
+ Maryland:
83
+ id: 24
84
+ abbr: MD
85
+ name: Maryland
86
+ Massachusetts:
87
+ id: 25
88
+ abbr: MA
89
+ name: Massachusetts
90
+ Michigan:
91
+ id: 26
92
+ abbr: MI
93
+ name: Michigan
94
+ Minnesota:
95
+ id: 27
96
+ abbr: MN
97
+ name: Minnesota
98
+ Mississippi:
99
+ id: 28
100
+ abbr: MS
101
+ name: Mississippi
102
+ Missouri:
103
+ id: 29
104
+ abbr: MO
105
+ name: Missouri
106
+ Montana:
107
+ id: 30
108
+ abbr: MT
109
+ name: Montana
110
+ Nebraska:
111
+ id: 31
112
+ abbr: NE
113
+ name: Nebraska
114
+ Nevada:
115
+ id: 32
116
+ abbr: NV
117
+ name: Nevada
118
+ New Hampshire:
119
+ id: 33
120
+ abbr: NH
121
+ name: New Hampshire
122
+ New Jersey:
123
+ id: 34
124
+ abbr: NJ
125
+ name: New Jersey
126
+ New Mexico:
127
+ id: 35
128
+ abbr: NM
129
+ name: New Mexico
130
+ New York:
131
+ id: 36
132
+ abbr: NY
133
+ name: New York
134
+ North Carolina:
135
+ id: 37
136
+ abbr: NC
137
+ name: North Carolina
138
+ North Dakota:
139
+ id: 38
140
+ abbr: ND
141
+ name: North Dakota
142
+ Ohio:
143
+ id: 39
144
+ abbr: OH
145
+ name: Ohio
146
+ Oklahoma:
147
+ id: 40
148
+ abbr: OK
149
+ name: Oklahoma
150
+ Oregon:
151
+ id: 41
152
+ abbr: OR
153
+ name: Oregon
154
+ Pennsylvania:
155
+ id: 42
156
+ abbr: PA
157
+ name: Pennsylvania
158
+ Rhode Island:
159
+ id: 44
160
+ abbr: RI
161
+ name: Rhode Island
162
+ South Carolina:
163
+ id: 45
164
+ abbr: SC
165
+ name: South Carolina
166
+ South Dakota:
167
+ id: 46
168
+ abbr: SD
169
+ name: South Dakota
170
+ Tennessee:
171
+ id: 47
172
+ abbr: TN
173
+ name: Tennessee
174
+ Texas:
175
+ id: 48
176
+ abbr: TX
177
+ name: Texas
178
+ Utah:
179
+ id: 49
180
+ abbr: UT
181
+ name: Utah
182
+ Vermont:
183
+ id: 50
184
+ abbr: VT
185
+ name: Vermont
186
+ Virginia:
187
+ id: 51
188
+ abbr: VA
189
+ name: Virginia
190
+ Washington:
191
+ id: 53
192
+ abbr: WA
193
+ name: Washington
194
+ West Virginia:
195
+ id: 54
196
+ abbr: WV
197
+ name: West Virginia
198
+ Wisconsin:
199
+ id: 55
200
+ abbr: WI
201
+ name: Wisconsin
202
+ Wyoming:
203
+ id: 56
204
+ abbr: WY
205
+ name: Wyoming
206
+ Puerto Rico:
207
+ id: 72
208
+ abbr: PR
209
+ name: Puerto Rico