us_geo 1.0.3 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +88 -0
  3. data/MIT_LICENSE.txt +21 -0
  4. data/README.md +77 -60
  5. data/UPDATING_TO_VERSION_2.md +172 -0
  6. data/VERSION +1 -0
  7. data/db/migrate/20190221054200_create_regions.rb +4 -2
  8. data/db/migrate/20190221054300_create_divisions.rb +4 -2
  9. data/db/migrate/20190221054400_create_states.rb +4 -2
  10. data/db/migrate/20190221054500_create_combined_statistical_areas.rb +4 -2
  11. data/db/migrate/20190221054600_create_core_based_statistical_areas.rb +4 -2
  12. data/db/migrate/20190221054650_create_metropolitan_divisions.rb +4 -2
  13. data/db/migrate/20190221054700_create_counties.rb +4 -3
  14. data/db/migrate/20190221054800_create_zctas.rb +4 -2
  15. data/db/migrate/20190221054900_create_zcta_counties.rb +4 -2
  16. data/db/migrate/20190221055000_create_urban_areas.rb +4 -2
  17. data/db/migrate/20190221055100_create_urban_area_counties.rb +4 -4
  18. data/db/migrate/20190221055200_create_zcta_urban_areas.rb +4 -4
  19. data/db/migrate/20190221060000_create_places.rb +4 -2
  20. data/db/migrate/20190221061000_create_place_counties.rb +4 -2
  21. data/db/migrate/20190221062000_create_zcta_places.rb +4 -4
  22. data/db/migrate/20190221063000_create_county_subdivisions.rb +4 -2
  23. data/db/migrate/20220722000000_allow_null_zcta_counties_demographics.rb +23 -0
  24. data/db/migrate/20220722000200_allow_null_zcta_places_demographics.rb +23 -0
  25. data/db/migrate/20230414000000_add_zcta_primary_place.rb +13 -0
  26. data/db/migrate/20230414000200_add_demographics_to_regions.rb +28 -0
  27. data/db/migrate/20230414000300_add_demographics_to_divisions.rb +28 -0
  28. data/db/migrate/20230414000400_add_demographics_to_states.rb +28 -0
  29. data/db/migrate/20230414000700_add_short_name_to_core_based_statistical_areas.rb +22 -0
  30. data/db/migrate/20230414000750_add_short_name_to_combined_statistical_areas.rb +22 -0
  31. data/db/migrate/20230414000800_create_zcta_mappings.rb +18 -0
  32. data/db/migrate/20230417000100_create_zcta_county_subdivisions.rb +22 -0
  33. data/db/migrate/20230417000200_add_unique_name_index_to_county_subdivisions.rb +26 -0
  34. data/db/migrate/20230417000250_add_zcta_primary_county_subdivision.rb +13 -0
  35. data/db/migrate/20230417000300_create_urban_area_county_subdivisions.rb +23 -0
  36. data/db/migrate/20230417000400_allow_null_urban_area_counties_demographics.rb +23 -0
  37. data/db/migrate/20230417000500_allow_null_zcta_urban_areas_demographics.rb +23 -0
  38. data/db/migrate/20230417000600_add_additional_time_zone_name_to_counties.rb +13 -0
  39. data/db/migrate/20230426000100_add_index_on_zctas_geoids.rb +18 -0
  40. data/db/schema.rb +303 -0
  41. data/explorer_app/.gitattributes +7 -0
  42. data/explorer_app/.gitignore +34 -0
  43. data/explorer_app/Gemfile +38 -0
  44. data/explorer_app/Rakefile +6 -0
  45. data/explorer_app/app/assets/images/.keep +0 -0
  46. data/explorer_app/app/assets/stylesheets/application.css +1 -0
  47. data/explorer_app/app/controllers/application_controller.rb +64 -0
  48. data/explorer_app/app/controllers/combined_statistical_areas_controller.rb +12 -0
  49. data/explorer_app/app/controllers/concerns/.keep +0 -0
  50. data/explorer_app/app/controllers/core_based_statistical_areas_controller.rb +36 -0
  51. data/explorer_app/app/controllers/counties_controller.rb +22 -0
  52. data/explorer_app/app/controllers/county_subdivisions_controller.rb +23 -0
  53. data/explorer_app/app/controllers/divisions_controller.rb +27 -0
  54. data/explorer_app/app/controllers/home_controller.rb +6 -0
  55. data/explorer_app/app/controllers/metropolitan_divisions_controller.rb +27 -0
  56. data/explorer_app/app/controllers/places_controller.rb +23 -0
  57. data/explorer_app/app/controllers/regions_controller.rb +13 -0
  58. data/explorer_app/app/controllers/states_controller.rb +25 -0
  59. data/explorer_app/app/controllers/urban_areas_controller.rb +24 -0
  60. data/explorer_app/app/controllers/zctas_controller.rb +23 -0
  61. data/explorer_app/app/helpers/application_helper.rb +137 -0
  62. data/explorer_app/app/models/application_record.rb +3 -0
  63. data/explorer_app/app/models/concerns/.keep +0 -0
  64. data/explorer_app/app/views/combined_statistical_areas/_table.html.erb +18 -0
  65. data/explorer_app/app/views/combined_statistical_areas/index.html.erb +7 -0
  66. data/explorer_app/app/views/combined_statistical_areas/show.html.erb +52 -0
  67. data/explorer_app/app/views/core_based_statistical_areas/_table.html.erb +32 -0
  68. data/explorer_app/app/views/core_based_statistical_areas/index.html.erb +19 -0
  69. data/explorer_app/app/views/core_based_statistical_areas/show.html.erb +60 -0
  70. data/explorer_app/app/views/counties/_table.html.erb +64 -0
  71. data/explorer_app/app/views/counties/show.html.erb +96 -0
  72. data/explorer_app/app/views/county_subdivisions/_table.html.erb +48 -0
  73. data/explorer_app/app/views/county_subdivisions/show.html.erb +84 -0
  74. data/explorer_app/app/views/divisions/_table.html.erb +24 -0
  75. data/explorer_app/app/views/divisions/index.html.erb +3 -0
  76. data/explorer_app/app/views/divisions/show.html.erb +3 -0
  77. data/explorer_app/app/views/home/index.html.erb +23 -0
  78. data/explorer_app/app/views/layouts/application.html.erb +34 -0
  79. data/explorer_app/app/views/metropolitan_divisions/_table.html.erb +42 -0
  80. data/explorer_app/app/views/metropolitan_divisions/index.html.erb +8 -0
  81. data/explorer_app/app/views/metropolitan_divisions/show.html.erb +46 -0
  82. data/explorer_app/app/views/places/_table.html.erb +47 -0
  83. data/explorer_app/app/views/places/show.html.erb +92 -0
  84. data/explorer_app/app/views/regions/_table.html.erb +18 -0
  85. data/explorer_app/app/views/regions/index.html.erb +7 -0
  86. data/explorer_app/app/views/regions/show.html.erb +3 -0
  87. data/explorer_app/app/views/shared/_breadcrumbs.html.erb +13 -0
  88. data/explorer_app/app/views/shared/_demographics_cells.html.erb +5 -0
  89. data/explorer_app/app/views/shared/_demographics_headers.html.erb +5 -0
  90. data/explorer_app/app/views/states/_table.html.erb +31 -0
  91. data/explorer_app/app/views/states/index.html.erb +7 -0
  92. data/explorer_app/app/views/states/show.html.erb +54 -0
  93. data/explorer_app/app/views/urban_areas/_table.html.erb +45 -0
  94. data/explorer_app/app/views/urban_areas/index.html.erb +19 -0
  95. data/explorer_app/app/views/urban_areas/show.html.erb +68 -0
  96. data/explorer_app/app/views/zctas/_table.html.erb +60 -0
  97. data/explorer_app/app/views/zctas/show.html.erb +104 -0
  98. data/explorer_app/bin/bundle +109 -0
  99. data/explorer_app/bin/rails +4 -0
  100. data/explorer_app/bin/rake +4 -0
  101. data/explorer_app/bin/setup +33 -0
  102. data/explorer_app/config/application.rb +37 -0
  103. data/explorer_app/config/boot.rb +3 -0
  104. data/explorer_app/config/database.yml +13 -0
  105. data/explorer_app/config/environment.rb +5 -0
  106. data/explorer_app/config/environments/development.rb +59 -0
  107. data/explorer_app/config/initializers/filter_parameter_logging.rb +8 -0
  108. data/explorer_app/config/locales/en.yml +2 -0
  109. data/explorer_app/config/puma.rb +43 -0
  110. data/explorer_app/config/routes.rb +56 -0
  111. data/explorer_app/config.ru +6 -0
  112. data/explorer_app/db/seeds.rb +7 -0
  113. data/explorer_app/lib/assets/.keep +0 -0
  114. data/explorer_app/lib/tasks/.keep +0 -0
  115. data/explorer_app/log/.keep +0 -0
  116. data/explorer_app/public/404.html +67 -0
  117. data/explorer_app/public/422.html +67 -0
  118. data/explorer_app/public/500.html +66 -0
  119. data/explorer_app/public/apple-touch-icon-precomposed.png +0 -0
  120. data/explorer_app/public/apple-touch-icon.png +0 -0
  121. data/explorer_app/public/favicon.ico +0 -0
  122. data/explorer_app/public/robots.txt +1 -0
  123. data/explorer_app/tmp/.keep +0 -0
  124. data/explorer_app/tmp/pids/.keep +0 -0
  125. data/lib/tasks/us_geo/us_geo.rake +44 -3
  126. data/lib/us_geo/area.rb +44 -0
  127. data/lib/us_geo/base_record.rb +22 -16
  128. data/lib/us_geo/combined_statistical_area.rb +18 -8
  129. data/lib/us_geo/core_based_statistical_area.rb +24 -12
  130. data/lib/us_geo/county.rb +67 -16
  131. data/lib/us_geo/county_subdivision.rb +43 -7
  132. data/lib/us_geo/division.rb +17 -7
  133. data/lib/us_geo/metropolitan_area.rb +1 -5
  134. data/lib/us_geo/metropolitan_division.rb +17 -9
  135. data/lib/us_geo/micropolitan_area.rb +1 -5
  136. data/lib/us_geo/place.rb +61 -11
  137. data/lib/us_geo/place_county.rb +1 -4
  138. data/lib/us_geo/population.rb +26 -0
  139. data/lib/us_geo/region.rb +18 -8
  140. data/lib/us_geo/state.rb +32 -11
  141. data/lib/us_geo/urban_area.rb +46 -16
  142. data/lib/us_geo/urban_area_county.rb +4 -21
  143. data/lib/us_geo/urban_area_county_subdivision.rb +51 -0
  144. data/lib/us_geo/urban_cluster.rb +1 -5
  145. data/lib/us_geo/urbanized_area.rb +1 -5
  146. data/lib/us_geo/version.rb +1 -1
  147. data/lib/us_geo/zcta.rb +90 -13
  148. data/lib/us_geo/zcta_county.rb +5 -22
  149. data/lib/us_geo/zcta_county_subdivision.rb +51 -0
  150. data/lib/us_geo/zcta_mapping.rb +35 -0
  151. data/lib/us_geo/zcta_place.rb +5 -12
  152. data/lib/us_geo/zcta_urban_area.rb +3 -20
  153. data/lib/us_geo.rb +34 -31
  154. data/us_geo.gemspec +36 -0
  155. metadata +126 -129
  156. data/Gemfile +0 -5
  157. data/Gemfile.lock +0 -75
  158. data/Rakefile +0 -18
  159. data/db/migrate/20190221054490_create_designated_market_areas.rb +0 -16
  160. data/lib/us_geo/demographics.rb +0 -25
  161. data/lib/us_geo/designated_market_area.rb +0 -30
  162. data/spec/spec_helper.rb +0 -22
  163. data/spec/us_geo/base_record_spec.rb +0 -67
  164. data/spec/us_geo/combined_statistical_area_spec.rb +0 -33
  165. data/spec/us_geo/core_based_statistical_area_spec.rb +0 -56
  166. data/spec/us_geo/county_spec.rb +0 -131
  167. data/spec/us_geo/county_subdivision_spec.rb +0 -37
  168. data/spec/us_geo/demographics_spec.rb +0 -19
  169. data/spec/us_geo/designated_market_area_spec.rb +0 -29
  170. data/spec/us_geo/division_spec.rb +0 -37
  171. data/spec/us_geo/metropolitan_division_spec.rb +0 -41
  172. data/spec/us_geo/place_county_spec.rb +0 -39
  173. data/spec/us_geo/place_spec.rb +0 -71
  174. data/spec/us_geo/region_spec.rb +0 -36
  175. data/spec/us_geo/state_spec.rb +0 -70
  176. data/spec/us_geo/urban_area_county_spec.rb +0 -82
  177. data/spec/us_geo/urban_area_spec.rb +0 -98
  178. data/spec/us_geo/zcta_county_spec.rb +0 -82
  179. data/spec/us_geo/zcta_place_spec.rb +0 -82
  180. data/spec/us_geo/zcta_spec.rb +0 -99
  181. data/spec/us_geo/zcta_urban_area_spec.rb +0 -82
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ .rails-default-error-page {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ .rails-default-error-page div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ .rails-default-error-page div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ .rails-default-error-page h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ .rails-default-error-page div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body class="rails-default-error-page">
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
File without changes
@@ -0,0 +1 @@
1
+ # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
File without changes
File without changes
@@ -6,7 +6,6 @@ namespace :us_geo do
6
6
  regions: USGeo::Region,
7
7
  divisions: USGeo::Division,
8
8
  states: USGeo::State,
9
- designated_market_areas: USGeo::DesignatedMarketArea,
10
9
  combined_statistical_areas: USGeo::CombinedStatisticalArea,
11
10
  core_based_statistical_areas: USGeo::CoreBasedStatisticalArea,
12
11
  metropolitan_divisions: USGeo::MetropolitanDivision,
@@ -17,18 +16,24 @@ namespace :us_geo do
17
16
  zctas: USGeo::Zcta,
18
17
  zcta_counties: USGeo::ZctaCounty,
19
18
  zcta_urban_areas: USGeo::ZctaUrbanArea,
19
+ zcta_county_subdivisions: USGeo::ZctaCountySubdivision,
20
20
  zcta_places: USGeo::ZctaPlace,
21
21
  urban_area_counties: USGeo::UrbanAreaCounty,
22
+ urban_area_county_subdivisions: USGeo::UrbanAreaCountySubdivision,
22
23
  place_counties: USGeo::PlaceCounty
23
24
  }
25
+
24
26
  klasses.each do |name, klass|
25
27
  desc "Import data for #{klass}"
26
28
  task name => :environment do
27
29
  t = Time.now
30
+
28
31
  klass.load!
29
32
  puts "Loaded #{klass.count} rows into #{klass.table_name} in #{(Time.now - t).round(1)}s"
30
- klass.removed.find_each do |record|
31
- puts(" WARNING: #{klass}.#{record.id} status changed to removed")
33
+
34
+ removed_count = klass.removed.count
35
+ if removed_count > 0
36
+ puts " #{removed_count} previously imported records in #{klass.table_name} no longer exist in the current data source"
32
37
  end
33
38
  end
34
39
 
@@ -39,5 +44,41 @@ namespace :us_geo do
39
44
  end
40
45
  end
41
46
  end
47
+
48
+ desc "List the number of records from previously imported data that no longer exists in the current data source"
49
+ task removed_counts: :environment do
50
+ klasses.each_value do |klass|
51
+ removed_count = klass.removed.count
52
+ puts "#{klass.table_name}: #{removed_count} previously imported records no longer exist in the current data source"
53
+ end
54
+ end
55
+
56
+ desc "Dump the data for all records from previously imported data that no longer exists in the current data source to JSON"
57
+ task dump_removed: :environment do
58
+ require "json"
59
+
60
+ puts "{"
61
+ klasses.each_value do |klass|
62
+ puts " \"#{klass.table_name}\": ["
63
+ klass.removed.find_each do |record|
64
+ row_json JSON.dump(record.attributes.except("status", "updated_at"))
65
+ puts "#{row_json},"
66
+ end
67
+ puts "]"
68
+ end
69
+ puts "}"
70
+ end
71
+
72
+ desc "Remove all records from previously imported data that no longer exists in the current data source"
73
+ task cleanup: :environment do
74
+ klasses.each_value do |klass|
75
+ count = 0
76
+ klass.removed.find_each do |record|
77
+ count += 1
78
+ record.destroy
79
+ end
80
+ puts "Deleted #{count} removed records from #{klass.table_name}"
81
+ end
82
+ end
42
83
  end
43
84
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module USGeo
4
+ # This module is mixed into all models. Note that the area given for land and water
5
+ # is in square miles.
6
+ module Area
7
+ SQUARE_MILES_TO_KILOMETERS = 2.59
8
+ private_constant :SQUARE_MILES_TO_KILOMETERS
9
+
10
+ # @!attribute land_area
11
+ # @return [Float, nil] Land area in square miles.
12
+
13
+ # @!attribute water_area
14
+ # @return [Integer, nil] Water area in square miles.
15
+
16
+ # Total area of both land an water in square miles.
17
+ #
18
+ # @return [Float, nil]
19
+ def total_area
20
+ land_area.to_f + water_area.to_f if land_area
21
+ end
22
+
23
+ # The fraction of the area that is composed of land instead of water.
24
+ #
25
+ # @return [Float, nil]
26
+ def percent_land
27
+ land_area / total_area if land_area
28
+ end
29
+
30
+ # Land area in square kilometers.
31
+ #
32
+ # @return [Float, nil]
33
+ def land_area_km
34
+ land_area * SQUARE_MILES_TO_KILOMETERS if land_area
35
+ end
36
+
37
+ # Water area in square kilometers.
38
+ #
39
+ # @return [Float, nil]
40
+ def water_area_km
41
+ water_area * SQUARE_MILES_TO_KILOMETERS if water_area
42
+ end
43
+ end
44
+ end
@@ -1,16 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "csv"
4
- require "open-uri"
5
-
6
3
  module USGeo
7
-
8
4
  class LoadError < StandardError
9
5
  end
10
6
 
11
7
  # Base class that all models inherit from.
12
8
  class BaseRecord < ::ActiveRecord::Base
13
-
14
9
  self.abstract_class = true
15
10
  self.table_name_prefix = "us_geo_"
16
11
 
@@ -23,7 +18,7 @@ module USGeo
23
18
  scope :imported, -> { where(status: STATUS_IMPORTED) }
24
19
  scope :removed, -> { where(status: STATUS_REMOVED) }
25
20
  scope :manual, -> { where(status: STATUS_MANUAL) }
26
- scope :not_removed, -> { where(status: [STATUS_IMPORTED, STATUS_MANUAL]) }
21
+ scope :not_removed, -> { where.not(status: STATUS_REMOVED) }
27
22
 
28
23
  class << self
29
24
  def load!(location = nil, gzipped: true)
@@ -61,11 +56,13 @@ module USGeo
61
56
  end
62
57
 
63
58
  def load_data_file(location, &block)
64
- file = nil
65
- if location.include?(":")
66
- file = URI.parse(location).open(read_timeout: 5, open_timeout: 5)
59
+ require "open-uri"
60
+ require "csv"
61
+
62
+ file = if location.include?(":")
63
+ URI.parse(location).open(read_timeout: 5, open_timeout: 5)
67
64
  else
68
- file = File.open(location)
65
+ File.open(location)
69
66
  end
70
67
  begin
71
68
  rows = []
@@ -81,24 +78,33 @@ module USGeo
81
78
  file.close if file && !file.closed?
82
79
  end
83
80
  end
84
-
85
- # Convert square meters to square miles
86
- def area_meters_to_miles(square_meters)
87
- (square_meters.to_f / (1609.34 ** 2)).round(6)
88
- end
89
81
  end
90
82
 
83
+ # @!attribute status
84
+ # @return [Integer]
85
+
86
+ # @!attribute updated_at
87
+ # @return [Time]
88
+
89
+ # Return true if the record was imported from the data source distributed with the gem.
90
+ #
91
+ # @return [Boolean]
91
92
  def imported?
92
93
  status == STATUS_IMPORTED
93
94
  end
94
95
 
96
+ # Return true if the record was removed from the data source distributed with the gem.
97
+ #
98
+ # @return [Boolean]
95
99
  def removed?
96
100
  status == STATUS_REMOVED
97
101
  end
98
102
 
103
+ # Return true if the record was manually added to the database.
104
+ #
105
+ # @return [Boolean]
99
106
  def manual?
100
107
  status == STATUS_MANUAL
101
108
  end
102
-
103
109
  end
104
110
  end
@@ -1,24 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module USGeo
4
-
5
4
  # Combined statistical area (CSA) of multiple metropolitan areas with weak regional
6
5
  # and economic connectoins between them.
7
6
  class CombinedStatisticalArea < BaseRecord
8
-
9
- include Demographics
7
+ include Population
8
+ include Area
10
9
 
11
10
  self.primary_key = "geoid"
12
11
 
13
- has_many :core_based_statistical_areas, foreign_key: :csa_geoid, inverse_of: :combined_statistical_area
12
+ has_many :core_based_statistical_areas, -> { not_removed }, foreign_key: :csa_geoid, inverse_of: :combined_statistical_area
13
+ has_many :counties, -> { not_removed }, through: :core_based_statistical_areas
14
+ has_many :metropolitan_divisions, -> { not_removed }, through: :core_based_statistical_areas
14
15
 
15
16
  validates :geoid, length: {is: 3}
16
- validates :name, length: {maximum: 60}
17
+ validates :name, presence: true, length: {maximum: 60}, uniqueness: true
17
18
  validates :land_area, numericality: true, presence: true
18
19
  validates :water_area, numericality: true, presence: true
19
20
  validates :population, numericality: {only_integer: true}, presence: true
20
21
  validates :housing_units, numericality: {only_integer: true}, presence: true
21
22
 
23
+ # @!attribute geoid
24
+ # @return [String] 3-digit code for the CSA.
25
+
26
+ # @!attribute name
27
+ # @return [String] Name of the CSA.
28
+
29
+ # @!attribute short_name
30
+ # @return [String] Short name of the CSA.
31
+
22
32
  class << self
23
33
  def load!(uri = nil)
24
34
  location = data_uri(uri || "combined_statistical_areas.csv")
@@ -26,15 +36,15 @@ module USGeo
26
36
  load_data_file(location) do |row|
27
37
  load_record!(geoid: row["GEOID"]) do |record|
28
38
  record.name = row["Name"]
39
+ record.short_name = row["Short Name"]
29
40
  record.population = row["Population"]
30
41
  record.housing_units = row["Housing Units"]
31
- record.land_area = area_meters_to_miles(row["Land Area"])
32
- record.water_area = area_meters_to_miles(row["Water Area"])
42
+ record.land_area = row["Land Area"]
43
+ record.water_area = row["Water Area"]
33
44
  end
34
45
  end
35
46
  end
36
47
  end
37
48
  end
38
-
39
49
  end
40
50
  end
@@ -1,42 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module USGeo
4
-
5
4
  # Core based statistical area composed of one or more counties anchored by an urban center.
6
5
  # Includes both metropolitan (population > 50,000) and micropolitan (population > 10,000
7
6
  # but < 50,000) areas.
8
7
  class CoreBasedStatisticalArea < BaseRecord
9
-
10
- include Demographics
8
+ include Population
9
+ include Area
11
10
 
12
11
  self.primary_key = "geoid"
13
12
  self.store_full_sti_class = false
14
13
 
15
- has_many :counties, foreign_key: :cbsa_geoid, inverse_of: :core_based_statistical_area
16
- has_many :metropolitan_divisions, foreign_key: :cbsa_geoid, inverse_of: :core_based_statistical_area
14
+ has_many :counties, -> { not_removed }, foreign_key: :cbsa_geoid, inverse_of: :core_based_statistical_area
15
+ has_many :metropolitan_divisions, -> { not_removed }, foreign_key: :cbsa_geoid, inverse_of: :core_based_statistical_area
16
+ has_many :zctas, -> { not_removed }, through: :counties
17
+ has_many :places, -> { not_removed }, through: :counties
18
+
17
19
  belongs_to :combined_statistical_area, foreign_key: :csa_geoid, optional: true, inverse_of: :core_based_statistical_areas
18
20
 
19
21
  validates :geoid, length: {is: 5}
20
- validates :name, length: {maximum: 60}
22
+ validates :name, presence: true, length: {maximum: 60}, uniqueness: true
23
+ validates :short_name, presence: true, length: {maximum: 60}, uniqueness: true
21
24
  validates :land_area, numericality: true, presence: true
22
25
  validates :water_area, numericality: true, presence: true
23
26
  validates :population, numericality: {only_integer: true}, presence: true
24
27
  validates :housing_units, numericality: {only_integer: true}, presence: true
25
28
 
29
+ # @!attribute geoid
30
+ # @return [String] 5-digit code for the CBSA.
31
+
32
+ # @!attribute name
33
+ # @return [String] Name of the CBSA.
34
+
35
+ # @!attribute short_name
36
+ # @return [String] Short name of the CBSA.
37
+
26
38
  class << self
27
39
  def load!(uri = nil)
28
40
  location = data_uri(uri || "core_based_statistical_areas.csv")
29
-
41
+
30
42
  import! do
31
43
  load_data_file(location) do |row|
32
44
  load_record!(geoid: row["GEOID"]) do |record|
33
- record.type = (row["Population"].to_i >= 50_000 ? "MetropolitanArea" : "MicropolitanArea")
45
+ record.type = ((row["Population"].to_i >= 50_000) ? "MetropolitanArea" : "MicropolitanArea")
34
46
  record.name = row["Name"]
47
+ record.short_name = row["Short Name"]
35
48
  record.csa_geoid = row["CSA"]
36
49
  record.population = row["Population"]
37
50
  record.housing_units = row["Housing Units"]
38
- record.land_area = area_meters_to_miles(row["Land Area"])
39
- record.water_area = area_meters_to_miles(row["Water Area"])
51
+ record.land_area = row["Land Area"]
52
+ record.water_area = row["Water Area"]
40
53
  record.lat = row["Latitude"]
41
54
  record.lng = row["Longitude"]
42
55
  end
@@ -48,10 +61,9 @@ module USGeo
48
61
  def metropolitan?
49
62
  raise NotImplementedError
50
63
  end
51
-
64
+
52
65
  def micropolitan?
53
66
  raise NotImplementedError
54
67
  end
55
-
56
68
  end
57
69
  end