class Pippa::Map

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).

Constants

TWO_SQRT_1_PI

Attributes

dot_kind[RW]

Dot shape. Either :square (default) or :circle

fill[RW]

Dot fill color

fill_opacity[RW]

Dot fill opacity

height[R]

Height of the map image in pixels

image[R]

RMagick image for direct manipulation, for example drawing lines and labels

merge[RW]

Boolean saying whether to merge markers to eliminate overlaps during rendering.

point_size[RW]

Base size of dot edges in pixels; defaults to 1. Therefore a unit area is one pixel.

stroke[RW]

Dot border stroke color name

stroke_width[RW]

Dot border stroke width

width[R]

Width of the map image in pixels

Public Class Methods

new(name = 'World') click to toggle source

Make a new map with given name. See the file maps/_info or call Pippa#map_names for all possible.

# 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
write_zipcode_maps(dot_kind = :circle) click to toggle source

Write the test map produced by zipcode_map as png and jpg files.

# 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) click to toggle source

Make a map showing all the zip codes in the USA with dots of fixed area. Also a couple of additional dots.

# 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

Public Instance Methods

add_at_lat_lon(lat, lon, area = 0) click to toggle source

Add a dot on the map at given latitude and longitude with given area.

Attributes

  • lat - Dot latitude

  • lon - Dot longitude

  • area - Optional area, defaults to single pixel

Examples

Make a map and put a dot at West Point, NY.

map = Map.new('USA')
map.add_at_lat_lon(41, -74, 100)
map.write_png('map.png')
# 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
Also aliased as: add_dot_at_lat_lon
add_at_zip(zip, area = 0) click to toggle source

Add a dot on the map at given 5-digit zip code.

Attributes

  • zip - Zipcode

  • area - Optional area, defaults to single pixel

Examples

Make a map and put a dot at West Point, NY.

map = Map.new('USA')
map.add_at_zip('10996', 100)
map.write_png('map.png')
# 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
Also aliased as: add_dot_at_zip
add_dot(x, y, area = 0) click to toggle source

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')
# File lib/pippa.rb, line 180
def add_dot(x, y, area = 0)
  @dots << [x, y, area]
end
add_dot_at_lat_lon(lat, lon, area = 0)
Alias for: add_at_lat_lon
add_dot_at_zip(zip, area = 0)
Alias for: add_at_zip
lat_lon_to_xy(lat, lon) click to toggle source

Return the pixel-xy coordinate on this map of a given latitude and longitude.

Attributes

  • lat - Given latitude

  • lon - Given longitude

Examples

Get the pixel coordinate of West Point, NY.

map = Map.new('USA')
x, y = map.lat_lon_to_xy(41, -74)
# 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() click to toggle source
# 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() click to toggle source

Force rendering of all dots added so far onto the map. Then forget them so they’re never rendered again.

# 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) click to toggle source

Return true iff we respond to given method. Takes care of to_??? and write_???? converters and writers of graphic formats.

Calls superclass method
# File lib/pippa.rb, line 286
def respond_to? (sym, include_private = false)
  conversion_to_format(sym) || writer_to_format(sym) ? true : super
end
to_xxx() click to toggle source

Return map as a blob with Magick format xxx. Get a full list of formats with this:

Magick.formats.each {|k,v| puts k if v.include?('*') }
# File lib/pippa.rb, line 300
    
write_xxx(filename) click to toggle source

Write map as graphic file in Magick format xxx. File suffix is not added automatically. Get a full list of formats with this:

Magick.formats.each {|k,v| puts k if v.include?('w') }
# File lib/pippa.rb, line 291