census_shapes 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,164 @@
1
+ class MODEL_NAME < ActiveRecord::Base
2
+
3
+ alias_attribute :id, :geoid10
4
+ alias_attribute :name, :name10
5
+ alias_attribute :region, :region10
6
+ alias_attribute :division, :division10
7
+ alias_attribute :state, :state10
8
+ alias_attribute :statens, :statens10
9
+ alias_attribute :statefp, :statefp10
10
+ alias_attribute :countyfp, :countyfp10
11
+ alias_attribute :tractce, :tractce10
12
+ alias_attribute :blkgrpce, :blkgrpce10
13
+ alias_attribute :stusps, :stusps10
14
+ alias_attribute :lsad, :lsad10
15
+ alias_attribute :mtcc10, :mtfcc10
16
+ alias_attribute :funcstat, :funcstat10
17
+ alias_attribute :land, :aland10
18
+ alias_attribute :water, :awater10
19
+ alias_attribute :lat, :intptlat10
20
+ alias_attribute :lng, :intptlon10
21
+
22
+ self.primary_key = :gid
23
+
24
+ default_scope select("geoid10, name10, intptlat10, intptlon10, latlng, geom")
25
+
26
+ def self.bbox(z,x,y)
27
+ return false if z.nil? || x.nil? || y.nil?
28
+ nw=coordinateLocation(z.to_i,x.to_i,y.to_i)
29
+ se=coordinateLocation(z.to_i,x.to_i+1,y.to_i+1)
30
+ return "#{nw[:lng]},#{se[:lat]},#{se[:lng]},#{nw[:lat]}"
31
+ end
32
+
33
+ def self.coordinateLocation(z,x,y)
34
+ k = 45 / 2 ** (z - 3.001)
35
+ return {:lng=> (k * x - 180).to_i,:lat=> y2lat(180 - k * y)}
36
+ end
37
+
38
+ def self.y2lat(y)
39
+ return (360 / Math::PI * Math.atan(Math.exp(y * Math::PI / 180)) -90).round(2)
40
+ end
41
+
42
+
43
+ def self.features(options={})
44
+ features = []; i =1;
45
+ options[:detail]=0.01 if options[:detail].nil?
46
+ model = options[:type].capitalize.constantize
47
+ if !options[:detail].nil?
48
+ geos = model.select("name10, geoid10, ST_AsGeoJson(ST_SimplifyPreserveTopology(geom,#{options[:detail]})) AS geojson");
49
+ else
50
+ geos = model.select("name10, geoid10, ST_AsGeoJson(geom) As geojson, geom, ST_Area(geom) AS area, ST_IsValid(geom) AS isvalid");
51
+ end
52
+ if(options[:bbox])
53
+ b = options[:bbox].split(",")
54
+ ids = model.select("geoid10").where("ST_Contains(ST_AsText(ST_Envelope('LINESTRING(#{b[0]} #{b[1]},#{b[2]} #{b[3]})'::geometry)), ST_AsText(latlng))").map{|id| id['geoid10']}
55
+ if options[:grid]
56
+ geobox = model.select("ST_AsGeoJson(ST_Envelope('LINESTRING(#{b[0]} #{b[1]},#{b[2]} #{b[3]})'::geometry)) AS geojson").first
57
+ features << {:type => "Feature", :geometry=> JSON.parse(geobox['geojson']), :properties => {:id=> "1", :name => "Box", :class=> 'box'}}
58
+ end
59
+ if ids.empty?
60
+ geos = []
61
+ else
62
+ geos = geos.where(:geoid10 => ids)
63
+ end
64
+ end
65
+ if(options[:id])
66
+ geos = geos.where(:geoid10=>options[:id].split(","))
67
+ end
68
+ for g in geos
69
+ if g['isvalid'] != "t" && options[:fix_geom]
70
+ new_g = g.fix_geometry
71
+ msg = "Geometry invalid. Rebuilding. Original = #{g['isvalid'] == "t"}, Rebuilt Geometry = #{new_g['isvalid'] == "t"}, Old area (#{g['area']}) == New area (#{new_g['area']})"
72
+ features << {:type => "Feature", :geometry=> JSON.parse(new_g['geojson']), :properties => {:id=> g.geoid10, :name => g.name10 }}
73
+ else
74
+ features << {:type => "Feature", :geometry=> JSON.parse(g['geojson']), :properties => {:id=> g.geoid10, :name => g.name10 }}
75
+ end
76
+ i=i+1
77
+ end
78
+ return features
79
+ end
80
+
81
+ def fix_geometry
82
+ return self if self['isvalid'] == "t"
83
+ rebuilt_g = MODEL_NAME.rebuild(self.polygons)
84
+ return rebuilt_g if rebuilt_g['isvalid'] == "t"
85
+ buffer_g = self.buffer
86
+ return buffer_g if buffer_g['isvalid'] == "t"
87
+ puts "ERROR GEOMETRY NOT FIXED"
88
+ return false
89
+ end
90
+
91
+ def buffer
92
+ return self.class.name.constantize.select("geoid10, ST_Buffer(geom,.000001) AS geo, ST_AsGeoJson(ST_Buffer(geom,.000001)) AS geojson, ST_IsValid(ST_Buffer(geom,.000001)) AS isvalid, ST_Area(ST_Buffer(geom,.000001)) AS area").where(:geoid10 => self.geoid10).first
93
+ end
94
+
95
+ def polygons
96
+ return self.class.name.constantize.select("ST_AsText((ST_DumpRings(((ST_Dump(geom)).geom))).geom) AS g").where(:geoid10 => self.geoid10).map{|p| p['g'] }
97
+ end
98
+
99
+ def self.sort(polygons)
100
+ return polygons.map{|p|
101
+ pp = ActiveRecord::Base.connection.execute("SELECT ST_Area('#{p}'), ST_AsText('#{p}') AS geo").first
102
+ [pp['st_area'].to_f, pp['geo'] ]}.sort {|a, b| a[0] <=> b[0]}.reverse.map{|p| p[1]
103
+ }
104
+ end
105
+
106
+ def self.rebuild(polygons)
107
+ polygons = MODEL_NAME.sort(polygons)
108
+ polygons.reverse.each do |p1|
109
+ polygons.each do |p2|
110
+ if p1 != p2 && ActiveRecord::Base.connection.execute("SELECT ST_Contains('#{p1}', '#{p2}') AS contains").first['contains'] == "t"
111
+ temp = ActiveRecord::Base.connection.execute("SELECT ST_AsText(ST_Difference('#{p1}', '#{p2}')) AS union")
112
+ polygons.delete(p1); polygons.delete(p2)
113
+ polygons << temp.first['union']
114
+ end
115
+ end
116
+ end
117
+ polygons = MODEL_NAME.merge(polygons)
118
+ return polygons
119
+ end
120
+
121
+ def self.merge(polygons)
122
+ if polygons.count == 1
123
+ return polygons[0]
124
+ else
125
+ array = polygons.map { |p| "ST_GeomFromText('#{p}')" }
126
+ geo = ActiveRecord::Base.connection.execute("SELECT ST_Collect(ARRAY[#{array.join(',')}]) AS geo").first['geo']
127
+ sql = "SELECT '#{geo}' AS geo, ST_AsGeoJson('#{geo}') AS geojson, ST_Area('#{geo}') AS area, ST_IsValid('#{geo}') AS isvalid;"
128
+ return ActiveRecord::Base.connection.execute(sql).first
129
+ end
130
+ end
131
+
132
+ def self.available_shapes
133
+ return @available_shapes if @available_shapes
134
+ @available_shapes = CENSUS_SHAPES
135
+ available_types = MODEL_NAME.unscoped.select("DISTINCT(type)").map{|k,v| k['type'].upcase}
136
+ @available_shapes.each do |k,v|
137
+ @available_shapes.delete(k) if !available_types.include?(k)
138
+ end
139
+ return @available_shapes
140
+ end
141
+ end
142
+
143
+ class State < MODEL_NAME; end
144
+ class County < MODEL_NAME; end
145
+ class Cousub < MODEL_NAME; end
146
+ class Submcd < MODEL_NAME; end
147
+ class Block < MODEL_NAME; end
148
+ class Tract < MODEL_NAME; end
149
+ class BG < MODEL_NAME; end
150
+ class Place < MODEL_NAME; end
151
+ class Anrc < MODEL_NAME; end
152
+ class Aiannh < MODEL_NAME; end
153
+ class Aits < MODEL_NAME; end
154
+ class Cbsa < MODEL_NAME; end
155
+ class Metdiv < MODEL_NAME; end
156
+ class Csa < MODEL_NAME; end
157
+ class Cd < MODEL_NAME; end
158
+ class Sldu < MODEL_NAME; end
159
+ class Sldl < MODEL_NAME; end
160
+ class Vtd < MODEL_NAME; end
161
+ class Zcta5 < MODEL_NAME; end
162
+ class Elsd < MODEL_NAME; end
163
+ class Scsd < MODEL_NAME; end
164
+ class Unsd < MODEL_NAME; end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="content-type" content="text/html;charset=utf-8">
5
+ <title>Census Shapes Polymaps Example</title>
6
+ <!-- POLYMAPS REFERENCE FILES -->
7
+ <script type="text/javascript" src="http://polymaps.org/polymaps.min.js?2.5.0"></script>
8
+ <script type="text/javascript" src="http://polymaps.org/nns.min.js?1.1.0"></script>
9
+ <link rel="stylesheet" type="text/css" href="http://polymaps.org/screen.css?0.9+1" />
10
+ <link rel="stylesheet" type="text/css" href="http://polymaps.org/style.css?2.2.0" />
11
+ </head>
12
+ <body>
13
+ <div class="container">
14
+ <%= yield %>
15
+ </div>
16
+ </body>
17
+ </html>
@@ -0,0 +1,89 @@
1
+ <div class="span-18 last top">
2
+ <a href="http://polymaps.org/">Census-Shapes</a>::
3
+ <%= select_tag :MODEL_NAME_type, options_for_select(MODEL_NAME.available_shapes.map{|g| [g[1]['name'], g[1]['slug']]}), :onchange => 'loadLayer()'%>
4
+ </div>
5
+ <div id="map" class="span-24 last"></div>
6
+ <style type="text/css">
7
+ .layer path{
8
+ fill: lightsteelblue;
9
+ fill-opacity: .5;
10
+ stroke: steelblue;
11
+ stroke-width: 1px;
12
+ }
13
+ </style>
14
+
15
+ <script type="text/javascript">
16
+
17
+ var po=org.polymaps,
18
+ map=po.map()
19
+ .container(document.getElementById("map")
20
+ .appendChild(po.svg("svg")))
21
+ .add(po.interact())
22
+ .add(po.hash());
23
+
24
+ map.add(
25
+ po.image()
26
+ .url(po.url("http://{S}tile.cloudmade.com/1a1b06b230af4efdbb989ea99e9841af/998/256/{Z}/{X}/{Y}.png")
27
+ .hosts(["a.","b.","c.",""])));
28
+
29
+ map.center({lat: 39, lon: -96}).zoom(4);
30
+ loadLayer();
31
+
32
+ function segment(template) {
33
+ return function(c) {
34
+ var max = 1 << c.zoom, column = c.column % max;
35
+ if (column < 0) column += max;
36
+ return template.replace(/{(.)}/g, function(s, v) {
37
+ switch (v) {
38
+ case "T":{
39
+ return document.getElementById('MODEL_NAME_type').value;
40
+ }
41
+ case "Z": return c.zoom;
42
+ case "X": return column;
43
+ case "Y": return c.row;
44
+ case "B": {
45
+ var nw = map.coordinateLocation({row: c.row, column: column, zoom: c.zoom}),
46
+ se = map.coordinateLocation({row: c.row + 1, column: column + 1, zoom: c.zoom}),
47
+ pn = Math.ceil(Math.log(c.zoom) / Math.LN2);
48
+ return nw.lon.toFixed(pn)
49
+ + "," + se.lat.toFixed(pn)
50
+ + "," + se.lon.toFixed(pn)
51
+ + "," + nw.lat.toFixed(pn);
52
+ }
53
+ }
54
+ return v;
55
+ });
56
+ };
57
+ }
58
+
59
+ function loadLayer(){
60
+ map_layer = po.geoJson()
61
+ .url(segment("/{T}/{Z}/{X}/{Y}.json"))
62
+ .on("load", load)
63
+ .id("map_layer")
64
+ .clip(false);
65
+ map.add(map_layer);
66
+
67
+ compass_layer = po.compass().pan("small");
68
+ map.add(compass_layer);
69
+ }
70
+
71
+ function load(e) {
72
+ for (var i = 0; i < e.features.length; i++) {
73
+ var feature = e.features[i];
74
+ var properties = feature.data.properties
75
+ feature.element.setAttribute("id", properties.id)
76
+ feature.element.setAttribute("name", properties.name)
77
+ feature.element.setAttribute("class", properties.class)
78
+ feature.element.appendChild(po.svg("title")
79
+ .appendChild(document.createTextNode(properties.name+" "+0+")"))
80
+ .parentNode);
81
+ }
82
+ }
83
+
84
+
85
+
86
+ (function(){function d(){if(b=!b){c.style("position","fixed").style("border-width",0).style("width","100%").style("height","100%").style("top",0).style("left",0);a.style("position","fixed").style("right","16px").style("top","16px");e.attr("transform","translate(16,16)rotate(135)scale(5)translate(-1.85,0)");f.style("visibility","hidden").style("overflow","hidden")}else{c.style("position",null).style("border-width",null).style("width",null).style("height",null).style("top",null).style("left",null);
87
+ a.style("position","absolute").style("right","-16px").style("top","-16px");e.attr("transform","translate(16,16)rotate(-45)scale(5)translate(-1.85,0)");f.style("visibility",null).style("overflow",null)}map.resize()}var f=n$(document.body),c=n$("#map").style("visibility","visible"),b=false,a=c.add("svg:svg").attr("width",32).attr("height",32).style("position","absolute").style("right","-16px").style("top","-16px").style("visibility","visible").on("mousedown",d);a.add("svg:circle").attr("cx",16).attr("cy",
88
+ 16).attr("r",14).attr("fill","#fff").attr("stroke","#ccc").attr("stroke-width",4).add("svg:title").text("Toggle fullscreen. (ESC)");var e=a.add("svg:path").attr("transform","translate(16,16)rotate(-45)scale(5)translate(-1.85,0)").attr("d","M0,0L0,.5 2,.5 2,1.5 4,0 2,-1.5 2,-.5 0,-.5Z").attr("pointer-events","none").attr("fill","#aaa");window.addEventListener("keydown",function(g){g.keyCode==27&&b&&d()},false)})();
89
+ </script>
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'census_shapes'
5
+
6
+ RSpec.configure do |config|
7
+ # some (optional) config here
8
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: census_shapes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ty Rauber
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: pg
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: postgis_adapter
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: progress_bar
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: generator_spec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Imports all the US Census Geographies into a PostGIS database.
95
+ email:
96
+ - tyrauber@mac.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - Gemfile.lock
104
+ - LICENSE.txt
105
+ - README.md
106
+ - Rakefile
107
+ - census_shapes.gemspec
108
+ - lib/census_shapes.rb
109
+ - lib/census_shapes/version.rb
110
+ - lib/generators/census_shapes/setup/setup_generator.rb
111
+ - lib/generators/census_shapes/setup/templates/.DS_Store
112
+ - lib/generators/census_shapes/setup/templates/config/database_example.yml
113
+ - lib/generators/census_shapes/setup/templates/config/initializers/shapes_globals.rb
114
+ - lib/generators/census_shapes/setup/templates/controllers/shapes_controller.rb
115
+ - lib/generators/census_shapes/setup/templates/db/migrate/create_shapes.rb
116
+ - lib/generators/census_shapes/setup/templates/lib/tasks/census_shapes.rake
117
+ - lib/generators/census_shapes/setup/templates/lib/tasks/postgis_template.rake
118
+ - lib/generators/census_shapes/setup/templates/lib/yaml/us_shapes.yml
119
+ - lib/generators/census_shapes/setup/templates/lib/yaml/us_states.yml
120
+ - lib/generators/census_shapes/setup/templates/models/shape.rb
121
+ - lib/generators/census_shapes/setup/templates/views/layouts/shapes.html.erb
122
+ - lib/generators/census_shapes/setup/templates/views/shapes/index.html.erb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/tyrauber/census_shapes
125
+ licenses: []
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ required_rubygems_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ requirements: []
143
+ rubyforge_project: census_shapes
144
+ rubygems_version: 1.8.24
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: A Ruby Gem for importing US Census Shapes into PostGIS
148
+ test_files:
149
+ - spec/spec_helper.rb