us_geo 1.0.3 → 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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -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/schema.rb +303 -0
  40. data/explorer_app/.gitattributes +7 -0
  41. data/explorer_app/.gitignore +34 -0
  42. data/explorer_app/Gemfile +38 -0
  43. data/explorer_app/Rakefile +6 -0
  44. data/explorer_app/app/assets/images/.keep +0 -0
  45. data/explorer_app/app/assets/stylesheets/application.css +1 -0
  46. data/explorer_app/app/controllers/application_controller.rb +64 -0
  47. data/explorer_app/app/controllers/combined_statistical_areas_controller.rb +12 -0
  48. data/explorer_app/app/controllers/concerns/.keep +0 -0
  49. data/explorer_app/app/controllers/core_based_statistical_areas_controller.rb +36 -0
  50. data/explorer_app/app/controllers/counties_controller.rb +22 -0
  51. data/explorer_app/app/controllers/county_subdivisions_controller.rb +23 -0
  52. data/explorer_app/app/controllers/divisions_controller.rb +27 -0
  53. data/explorer_app/app/controllers/home_controller.rb +6 -0
  54. data/explorer_app/app/controllers/metropolitan_divisions_controller.rb +27 -0
  55. data/explorer_app/app/controllers/places_controller.rb +23 -0
  56. data/explorer_app/app/controllers/regions_controller.rb +13 -0
  57. data/explorer_app/app/controllers/states_controller.rb +25 -0
  58. data/explorer_app/app/controllers/urban_areas_controller.rb +24 -0
  59. data/explorer_app/app/controllers/zctas_controller.rb +23 -0
  60. data/explorer_app/app/helpers/application_helper.rb +137 -0
  61. data/explorer_app/app/models/application_record.rb +3 -0
  62. data/explorer_app/app/models/concerns/.keep +0 -0
  63. data/explorer_app/app/views/combined_statistical_areas/_table.html.erb +18 -0
  64. data/explorer_app/app/views/combined_statistical_areas/index.html.erb +7 -0
  65. data/explorer_app/app/views/combined_statistical_areas/show.html.erb +52 -0
  66. data/explorer_app/app/views/core_based_statistical_areas/_table.html.erb +32 -0
  67. data/explorer_app/app/views/core_based_statistical_areas/index.html.erb +19 -0
  68. data/explorer_app/app/views/core_based_statistical_areas/show.html.erb +60 -0
  69. data/explorer_app/app/views/counties/_table.html.erb +64 -0
  70. data/explorer_app/app/views/counties/show.html.erb +96 -0
  71. data/explorer_app/app/views/county_subdivisions/_table.html.erb +48 -0
  72. data/explorer_app/app/views/county_subdivisions/show.html.erb +84 -0
  73. data/explorer_app/app/views/divisions/_table.html.erb +24 -0
  74. data/explorer_app/app/views/divisions/index.html.erb +3 -0
  75. data/explorer_app/app/views/divisions/show.html.erb +3 -0
  76. data/explorer_app/app/views/home/index.html.erb +23 -0
  77. data/explorer_app/app/views/layouts/application.html.erb +34 -0
  78. data/explorer_app/app/views/metropolitan_divisions/_table.html.erb +42 -0
  79. data/explorer_app/app/views/metropolitan_divisions/index.html.erb +8 -0
  80. data/explorer_app/app/views/metropolitan_divisions/show.html.erb +46 -0
  81. data/explorer_app/app/views/places/_table.html.erb +47 -0
  82. data/explorer_app/app/views/places/show.html.erb +92 -0
  83. data/explorer_app/app/views/regions/_table.html.erb +18 -0
  84. data/explorer_app/app/views/regions/index.html.erb +7 -0
  85. data/explorer_app/app/views/regions/show.html.erb +3 -0
  86. data/explorer_app/app/views/shared/_breadcrumbs.html.erb +13 -0
  87. data/explorer_app/app/views/shared/_demographics_cells.html.erb +5 -0
  88. data/explorer_app/app/views/shared/_demographics_headers.html.erb +5 -0
  89. data/explorer_app/app/views/states/_table.html.erb +31 -0
  90. data/explorer_app/app/views/states/index.html.erb +7 -0
  91. data/explorer_app/app/views/states/show.html.erb +54 -0
  92. data/explorer_app/app/views/urban_areas/_table.html.erb +45 -0
  93. data/explorer_app/app/views/urban_areas/index.html.erb +19 -0
  94. data/explorer_app/app/views/urban_areas/show.html.erb +68 -0
  95. data/explorer_app/app/views/zctas/_table.html.erb +60 -0
  96. data/explorer_app/app/views/zctas/show.html.erb +104 -0
  97. data/explorer_app/bin/bundle +109 -0
  98. data/explorer_app/bin/rails +4 -0
  99. data/explorer_app/bin/rake +4 -0
  100. data/explorer_app/bin/setup +33 -0
  101. data/explorer_app/config/application.rb +37 -0
  102. data/explorer_app/config/boot.rb +3 -0
  103. data/explorer_app/config/database.yml +13 -0
  104. data/explorer_app/config/environment.rb +5 -0
  105. data/explorer_app/config/environments/development.rb +59 -0
  106. data/explorer_app/config/initializers/filter_parameter_logging.rb +8 -0
  107. data/explorer_app/config/locales/en.yml +2 -0
  108. data/explorer_app/config/puma.rb +43 -0
  109. data/explorer_app/config/routes.rb +56 -0
  110. data/explorer_app/config.ru +6 -0
  111. data/explorer_app/db/seeds.rb +7 -0
  112. data/explorer_app/lib/assets/.keep +0 -0
  113. data/explorer_app/lib/tasks/.keep +0 -0
  114. data/explorer_app/log/.keep +0 -0
  115. data/explorer_app/public/404.html +67 -0
  116. data/explorer_app/public/422.html +67 -0
  117. data/explorer_app/public/500.html +66 -0
  118. data/explorer_app/public/apple-touch-icon-precomposed.png +0 -0
  119. data/explorer_app/public/apple-touch-icon.png +0 -0
  120. data/explorer_app/public/favicon.ico +0 -0
  121. data/explorer_app/public/robots.txt +1 -0
  122. data/explorer_app/tmp/.keep +0 -0
  123. data/explorer_app/tmp/pids/.keep +0 -0
  124. data/lib/tasks/us_geo/us_geo.rake +44 -3
  125. data/lib/us_geo/area.rb +44 -0
  126. data/lib/us_geo/base_record.rb +22 -16
  127. data/lib/us_geo/combined_statistical_area.rb +18 -8
  128. data/lib/us_geo/core_based_statistical_area.rb +24 -12
  129. data/lib/us_geo/county.rb +67 -16
  130. data/lib/us_geo/county_subdivision.rb +43 -7
  131. data/lib/us_geo/division.rb +17 -7
  132. data/lib/us_geo/metropolitan_area.rb +1 -5
  133. data/lib/us_geo/metropolitan_division.rb +17 -9
  134. data/lib/us_geo/micropolitan_area.rb +1 -5
  135. data/lib/us_geo/place.rb +61 -11
  136. data/lib/us_geo/place_county.rb +1 -4
  137. data/lib/us_geo/population.rb +26 -0
  138. data/lib/us_geo/region.rb +18 -8
  139. data/lib/us_geo/state.rb +32 -11
  140. data/lib/us_geo/urban_area.rb +46 -16
  141. data/lib/us_geo/urban_area_county.rb +4 -21
  142. data/lib/us_geo/urban_area_county_subdivision.rb +51 -0
  143. data/lib/us_geo/urban_cluster.rb +1 -5
  144. data/lib/us_geo/urbanized_area.rb +1 -5
  145. data/lib/us_geo/version.rb +1 -1
  146. data/lib/us_geo/zcta.rb +90 -13
  147. data/lib/us_geo/zcta_county.rb +5 -22
  148. data/lib/us_geo/zcta_county_subdivision.rb +51 -0
  149. data/lib/us_geo/zcta_mapping.rb +35 -0
  150. data/lib/us_geo/zcta_place.rb +5 -12
  151. data/lib/us_geo/zcta_urban_area.rb +3 -20
  152. data/lib/us_geo.rb +34 -31
  153. data/us_geo.gemspec +36 -0
  154. metadata +125 -129
  155. data/Gemfile +0 -5
  156. data/Gemfile.lock +0 -75
  157. data/Rakefile +0 -18
  158. data/db/migrate/20190221054490_create_designated_market_areas.rb +0 -16
  159. data/lib/us_geo/demographics.rb +0 -25
  160. data/lib/us_geo/designated_market_area.rb +0 -30
  161. data/spec/spec_helper.rb +0 -22
  162. data/spec/us_geo/base_record_spec.rb +0 -67
  163. data/spec/us_geo/combined_statistical_area_spec.rb +0 -33
  164. data/spec/us_geo/core_based_statistical_area_spec.rb +0 -56
  165. data/spec/us_geo/county_spec.rb +0 -131
  166. data/spec/us_geo/county_subdivision_spec.rb +0 -37
  167. data/spec/us_geo/demographics_spec.rb +0 -19
  168. data/spec/us_geo/designated_market_area_spec.rb +0 -29
  169. data/spec/us_geo/division_spec.rb +0 -37
  170. data/spec/us_geo/metropolitan_division_spec.rb +0 -41
  171. data/spec/us_geo/place_county_spec.rb +0 -39
  172. data/spec/us_geo/place_spec.rb +0 -71
  173. data/spec/us_geo/region_spec.rb +0 -36
  174. data/spec/us_geo/state_spec.rb +0 -70
  175. data/spec/us_geo/urban_area_county_spec.rb +0 -82
  176. data/spec/us_geo/urban_area_spec.rb +0 -98
  177. data/spec/us_geo/zcta_county_spec.rb +0 -82
  178. data/spec/us_geo/zcta_place_spec.rb +0 -82
  179. data/spec/us_geo/zcta_spec.rb +0 -99
  180. 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