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).
Constant Summary collapse
- TWO_SQRT_1_PI =
2 * Math.sqrt(1 / Math::PI)
Instance Attribute Summary collapse
-
#dot_kind ⇒ Object
Dot shape.
-
#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.
-
#merge ⇒ Object
Boolean saying whether to merge markers to eliminate overlaps during rendering.
-
#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.
-
.write_zipcode_maps(dot_kind = :circle) ⇒ Object
Write the test map produced by
zipcode_map
as png and jpg files. -
.zipcode_map(dot_kind = :circle) ⇒ Object
Make a map showing all the zip codes in the USA with dots of fixed area.
Instance Method Summary collapse
-
#add_at_lat_lon(lat, lon, area = 0) ⇒ Object
(also: #add_dot_at_lat_lon)
Add a dot on the map at given latitude and longitude with given area.
-
#add_at_zip(zip, area = 0) ⇒ Object
(also: #add_dot_at_zip)
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.
- #merged_dots ⇒ Object
-
#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.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/pippa.rb', line 143 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 @dot_kind = :square @merge = 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.
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
# File 'lib/pippa.rb', line 306 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
#dot_kind ⇒ Object
Dot shape. Either :square (default) or :circle
74 75 76 |
# File 'lib/pippa.rb', line 74 def dot_kind @dot_kind end |
#fill ⇒ Object (readonly)
Dot fill color
93 94 95 |
# File 'lib/pippa.rb', line 93 def fill @fill end |
#fill_opacity ⇒ Object (readonly)
Dot fill opacity
99 100 101 |
# File 'lib/pippa.rb', line 99 def fill_opacity @fill_opacity end |
#height ⇒ Object (readonly)
Height of the map image in pixels
83 84 85 |
# File 'lib/pippa.rb', line 83 def height @height end |
#image ⇒ Object (readonly)
RMagick image for direct manipulation, for example drawing lines and labels
117 118 119 |
# File 'lib/pippa.rb', line 117 def image @image end |
#merge ⇒ Object
Boolean saying whether to merge markers to eliminate overlaps during rendering.
77 78 79 |
# File 'lib/pippa.rb', line 77 def merge @merge end |
#point_size ⇒ Object (readonly)
Base size of dot edges in pixels; defaults to 1. Therefore a unit area is one pixel.
87 88 89 |
# File 'lib/pippa.rb', line 87 def point_size @point_size end |
#stroke ⇒ Object (readonly)
Dot border stroke color name
105 106 107 |
# File 'lib/pippa.rb', line 105 def stroke @stroke end |
#stroke_width ⇒ Object (readonly)
Dot border stroke width
111 112 113 |
# File 'lib/pippa.rb', line 111 def stroke_width @stroke_width end |
#width ⇒ Object (readonly)
Width of the map image in pixels
80 81 82 |
# File 'lib/pippa.rb', line 80 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.
137 138 139 |
# File 'lib/pippa.rb', line 137 def self.info # :nodoc: @@info ||= info_from_file end |
.write_zipcode_maps(dot_kind = :circle) ⇒ Object
Write the test map produced by zipcode_map
as png and jpg files.
356 357 358 359 360 |
# File 'lib/pippa.rb', line 356 def self.write_zipcode_maps(dot_kind = :circle) m = zipcode_map(dot_kind) File.open('spec/data/zipcodes.png', 'wb') { |f| f.write(m.to_png) } m.write_jpg('spec/data/zipcodes.jpg') end |
.zipcode_map(dot_kind = :circle) ⇒ Object
Make a map showing all the zip codes in the USA with dots of fixed area. Also a couple of additional dots.
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/pippa.rb', line 339 def self.zipcode_map(dot_kind = :circle) generator = Random.new(42) # Force same on every run for testing. m = Map.new('USA') m.point_size = 1.5 m.dot_kind = dot_kind m.merge = true Pippa.zips.each_key.each do |zip| m.add_at_zip(zip, 1) 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 |
Instance Method Details
#add_at_lat_lon(lat, lon, area = 0) ⇒ Object Also known as: add_dot_at_lat_lon
217 218 219 |
# File 'lib/pippa.rb', line 217 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 Also known as: add_dot_at_zip
236 237 238 239 |
# File 'lib/pippa.rb', line 236 def add_at_zip(zip, area = 0) data = Pippa.zips[zip] add_at_lat_lon(data[:lat], data[:lon], 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')
180 181 182 |
# File 'lib/pippa.rb', line 180 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. We don’t handle this in method_missing because we need to detect boolean equivalence, not equality.
123 124 125 126 127 128 |
# File 'lib/pippa.rb', line 123 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.
131 132 133 |
# File 'lib/pippa.rb', line 131 def anti_alias? # :nodoc: @anti_alias end |
#lat_lon_to_xy(lat, lon) ⇒ Object
197 198 199 200 |
# File 'lib/pippa.rb', line 197 def lat_lon_to_xy(lat, lon) set_projection unless @lat_lon_to_xy @lat_lon_to_xy.call(lat, lon) end |
#merged_dots ⇒ Object
244 245 246 247 248 249 250 251 |
# File 'lib/pippa.rb', line 244 def merged_dots require 'lulu' list = Lulu::MarkerList.new list.set_info(@dot_kind, @point_size) @dots.each {|dot| list.add(*dot) } list.merge list.markers end |
#render ⇒ Object
Force rendering of all dots added so far onto the map. Then forget them so they’re never rendered again.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'lib/pippa.rb', line 255 def render return if @image.nil? || @dots.empty? if @merge @dots = merged_dots else @dots.sort! {|a, b| b[2] <=> a[2] } # by area, smallest last end gc = new_gc @dots.each do |x, y, area| diam = @point_size * Math.sqrt(area) diam *= TWO_SQRT_1_PI if @dot_kind == :circle x, y, diam = x.round, y.round, diam.round unless @anti_alias if diam <= 1 gc.point(x, y) else if @dot_kind == :circle gc.circle(x, y, x + diam / 2, y) else h = diam / 2 x1 = x - h y1 = y - h gc.rectangle(x1, y1, x1 + diam, y1 + diam) 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.
286 287 288 |
# File 'lib/pippa.rb', line 286 def respond_to? (sym, include_private = false) conversion_to_format(sym) || writer_to_format(sym) ? true : super end |