Class: Pippa::Map
Overview
An image-based map class that can be overlain with dots of given area and location given by pixel coordinates, lat/lon, or zipcode (courtesy of federalgovernmentzipcodes.us).
Instance Attribute Summary collapse
-
#fill ⇒ Object
readonly
Dot fill color.
-
#fill_opacity ⇒ Object
readonly
Dot fill opacity.
-
#height ⇒ Object
readonly
Height of the map image in pixels.
-
#image ⇒ Object
readonly
RMagick image for direct manipulation, for example drawing lines and labels.
-
#point_size ⇒ Object
readonly
Base size of dot edges in pixels; defaults to 1.
-
#stroke ⇒ Object
readonly
Dot border stroke color name.
-
#stroke_width ⇒ Object
readonly
Dot border stroke width.
-
#width ⇒ Object
readonly
Width of the map image in pixels.
Class Method Summary collapse
-
.info ⇒ Object
Return global map and projection information from config file.
-
.profile ⇒ Object
Run the profiler and record results.
-
.write_zipcode_maps ⇒ Object
Write the test map produced by
zipcode_map
as png and jpg files. -
.zipcode_map ⇒ Object
Make a map showing all the zip codes in the USA with dots of random size.
-
.zips ⇒ Object
Return a hash mapping zip codes to CSV records of zip code data.
Instance Method Summary collapse
-
#add_at_lat_lon(lat, lon, area = 0) ⇒ Object
Add a dot on the map at given latitude and longitude with given area.
-
#add_at_zip(zip, area = 0) ⇒ Object
Add a dot on the map at given 5-digit zip code.
-
#add_dot(x, y, area = 0) ⇒ Object
Add a dot of given area at the given pixel coordinates.
-
#anti_alias=(val) ⇒ Object
Render if we’re making a change and then set a flag indicating whether anti-aliasing will be performed in next render.
-
#anti_alias? ⇒ Boolean
Return flag indicating whether anti-aliasing will be performed in next render.
-
#initialize(name = 'World') ⇒ Map
constructor
Make a new map with given name.
-
#lat_lon_to_xy(lat, lon) ⇒ Object
Return the pixel-xy coordinate on this map of a given latitude and longitude.
-
#method_missing(sym, *args, &block) ⇒ Object
Handle special cases of missing converters, writers, and flushing attribute setters.
-
#render ⇒ Object
Force rendering of all dots added so far onto the map.
-
#respond_to?(sym, include_private = false) ⇒ Boolean
Return true iff we respond to given method.
Constructor Details
#initialize(name = 'World') ⇒ Map
Make a new map with given name. See the file maps/_info
or call Pippa#map_names for all possible.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/pippa.rb', line 88 def initialize(name = 'World') # Set up drawing standards. @point_size = 1 @fill = 'DarkRed' @stroke = 'gray25' @fill_opacity = 0.85 @stroke_width = 1 @anti_alias = false @dots = [] # Look up global info or return if none. return unless @map_info = Map.info[:map][name] @image = Image.read("#{File.dirname(__FILE__)}/pippa/maps/#{@map_info[0]}").first @width, @height = @image.columns, @image.rows # Look up projection info, if any. @projection_info = Map.info[:projection][name] end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(sym, *args, &block) ⇒ Object
Handle special cases of missing converters, writers, and flushing attribute setters.
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 |
# File 'lib/pippa.rb', line 266 def method_missing(sym, *args, &block) # :nodoc: # Handle graphic attribute setters. flushing with render first. if GRAPHIC_ATTRIBUTE_SETTERS.include?(sym) iv_name = "@#{sym.to_s[0..-2]}" old_val = instance_variable_get(iv_name) return old_val if args[0] == old_val render return instance_variable_set(iv_name, args[0]) end # Handle to_??? format converters, again flushing with render. fmt = conversion_to_format(sym) if fmt render @image.format = fmt return @image.to_blob end # Handle write_??? file writers, again flushing with render fmt = writer_to_format(sym) if fmt render @image.format = fmt return @image.write(args[0]) end # Punt on everything else. super end |
Instance Attribute Details
#fill ⇒ Object (readonly)
Dot fill color
39 40 41 |
# File 'lib/pippa.rb', line 39 def fill @fill end |
#fill_opacity ⇒ Object (readonly)
Dot fill opacity
45 46 47 |
# File 'lib/pippa.rb', line 45 def fill_opacity @fill_opacity end |
#height ⇒ Object (readonly)
Height of the map image in pixels
29 30 31 |
# File 'lib/pippa.rb', line 29 def height @height end |
#image ⇒ Object (readonly)
RMagick image for direct manipulation, for example drawing lines and labels
63 64 65 |
# File 'lib/pippa.rb', line 63 def image @image end |
#point_size ⇒ Object (readonly)
Base size of dot edges in pixels; defaults to 1. Therefore a unit area is one pixel.
33 34 35 |
# File 'lib/pippa.rb', line 33 def point_size @point_size end |
#stroke ⇒ Object (readonly)
Dot border stroke color name
51 52 53 |
# File 'lib/pippa.rb', line 51 def stroke @stroke end |
#stroke_width ⇒ Object (readonly)
Dot border stroke width
57 58 59 |
# File 'lib/pippa.rb', line 57 def stroke_width @stroke_width end |
#width ⇒ Object (readonly)
Width of the map image in pixels
26 27 28 |
# File 'lib/pippa.rb', line 26 def width @width end |
Class Method Details
.info ⇒ Object
Return global map and projection information from config file. See maps/_info
for format. This is not generally very useful.
82 83 84 |
# File 'lib/pippa.rb', line 82 def self.info # :nodoc: @@info ||= info_from_file end |
.profile ⇒ Object
Run the profiler and record results.
320 321 322 323 324 325 326 327 328 |
# File 'lib/pippa.rb', line 320 def self.profile require 'ruby-prof' RubyProf.start write_zipcode_maps result = RubyProf.stop File.open('profile.htm', 'w') do |f| RubyProf::GraphHtmlPrinter.new(result).print(f) end end |
.write_zipcode_maps ⇒ Object
Write the test map produced by zipcode_map
as png and jpg files.
313 314 315 316 317 |
# File 'lib/pippa.rb', line 313 def self.write_zipcode_maps m = zipcode_map File.open('spec/data/zipcodes.png', 'wb') { |f| f.write(m.to_png) } m.write_jpg('spec/data/zipcodes.jpg') end |
.zipcode_map ⇒ Object
Make a map showing all the zip codes in the USA with dots of random size. Also a couple of additional dots.
299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/pippa.rb', line 299 def self.zipcode_map generator = Random.new(42) # Force same on every run for testing. m = Map.new('USA') zips.each_key.each do |zip| m.add_at_zip(zip, generator.rand(4) ** 2) end m.fill = 'red' m.fill_opacity = 1 m.add_at_lat_lon(41, -74, 300) # West Point, NY m.add_at_lat_lon(38, -122, 300) # Berkeley, CA m end |
.zips ⇒ Object
Return a hash mapping zip codes to CSV records of zip code data. NB: The file is big, so this takes a while to return the first time called.
CSV::Row
struct format (see also ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Row.html):
#<CSV::Row
zipcode:"97475"
zip_code_type:"PO BOX"
city:"SPRINGFIELD"
state:"OR"
location_type:"PRIMARY"
lat:44.05
long:-123.02
location:"NA-US-OR-SPRINGFIELD"
decommisioned:"false"
tax_returns_filed:nil
estimated_population:nil
total_wages:nil>
See federalgovernmentzipcodes.us for more information on the zipcode data.
203 204 205 |
# File 'lib/pippa.rb', line 203 def self.zips @@zips ||= zips_from_file end |
Instance Method Details
#add_at_lat_lon(lat, lon, area = 0) ⇒ Object
160 161 162 |
# File 'lib/pippa.rb', line 160 def add_at_lat_lon(lat, lon, area = 0) add_dot(*lat_lon_to_xy(lat, lon), area) end |
#add_at_zip(zip, area = 0) ⇒ Object
178 179 180 181 |
# File 'lib/pippa.rb', line 178 def add_at_zip(zip, area = 0) data = Map.zips[zip] add_at_lat_lon(data[:lat], data[:long], area) if data end |
#add_dot(x, y, area = 0) ⇒ Object
Add a dot of given area at the given pixel coordinates.
Attributes
-
x
- Dot x-pixel coordinate -
y
- Dot y-pixel coordinate -
area
- Optional area, defaults to single pixel
Examples
Make a map and put a dot in the middle.
map = Map.new('USA')
map.add_dot(map.width/2, map.height/2, 100)
map.write_png('map.png')
123 124 125 |
# File 'lib/pippa.rb', line 123 def add_dot(x, y, area = 0) @dots << [x, y, area] end |
#anti_alias=(val) ⇒ Object
Render if we’re making a change and then set a flag indicating whether anti-aliasing will be performed in next render. Default is false.
68 69 70 71 72 73 |
# File 'lib/pippa.rb', line 68 def anti_alias=(val) # :nodoc: val = !!val return val if val == @anti_alias render @anti_alias = val end |
#anti_alias? ⇒ Boolean
Return flag indicating whether anti-aliasing will be performed in next render.
76 77 78 |
# File 'lib/pippa.rb', line 76 def anti_alias? # :nodoc: @anti_alias end |
#lat_lon_to_xy(lat, lon) ⇒ Object
140 141 142 143 |
# File 'lib/pippa.rb', line 140 def lat_lon_to_xy(lat, lon) set_projection unless @lat_lon_to_xy @lat_lon_to_xy.call(lat, lon) end |
#render ⇒ Object
Force rendering of all dots added so far onto the map. Then forget them so they’re never rendered again.
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 |
# File 'lib/pippa.rb', line 209 def render return if @image.nil? || @dots.empty? @dots.sort! {|a, b| b[2] <=> a[2] } # by area, smallest last gc = new_gc if @anti_alias @dots.each do |x, y, area| side = @point_size * Math.sqrt(area) if side <= 1 gc.point(x, y) else h = 0.5 * side x1 = x - h y1 = y - h gc.rectangle(x1, y1, x1 + side, y1 + side) end end else @dots.each do |x, y, area| side = @point_size * Math.sqrt(area) x, y, side = x.round, y.round, side.round if side <= 1 gc.point(x, y) else h = side / 2 x1 = x - h y1 = y - h gc.rectangle(x1, y1, x1 + side, y1 + side) end end end gc.draw(@image) @dots = [] end |
#respond_to?(sym, include_private = false) ⇒ Boolean
Return true iff we respond to given method. Takes care of to_??? and write_???? converters and writers of graphic formats.
246 247 248 |
# File 'lib/pippa.rb', line 246 def respond_to? (sym, include_private = false) conversion_to_format(sym) || writer_to_format(sym) ? true : super end |