smappy 0.0.1

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.
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source :rubygems
2
+
3
+ gemspec
4
+
5
+ gem 'rake'
6
+ gem 'rspec'
7
+ gem 'guard'
8
+ gem 'guard-bundler'
9
+ gem 'guard-rspec'
10
+ gem 'guard-spork'
11
+ gem 'rb-fsevent'
12
+ gem 'rb-readline'
13
+ gem 'fakeweb'
14
+ gem 'vcr'
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Eet.nu
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ Smappy
2
+ ------
@@ -0,0 +1,36 @@
1
+ require 'bundler'
2
+
3
+ Bundler.setup
4
+ Bundler.require
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
7
+ require 'smappy'
8
+
9
+ namespace :smappy do
10
+ desc "Generate an example static map"
11
+ task :generate_example do
12
+ # Compare this image to:
13
+ #
14
+ # * Google Maps: http://maps.google.com/maps/api/staticmap?size=500x350&sensor=false&center=50.9985099,5.857652&zoom=15
15
+ # * OpenStreetMap: http://staticmap.openstreetmap.de/staticmap.php?center=50.9985099,5.857652&zoom=15
16
+ #
17
+ map = Smappy::StaticMap.new(center: [50.9985099, 5.857652], zoomlevel: 15)
18
+
19
+ # Cloudmade tiles:
20
+ map.tile_url_template = 'http://a.tile.cloudmade.com/ed3b3505052b442dba7baff14f1ad671/47664/256/%{zoomlevel}/%{x}/%{y}.png'
21
+
22
+ # Google Maps tiles:
23
+ # map.tile_url_template = 'http://mt1.google.com/vt/x=%{x}&y=%{y}&z=%{zoomlevel}'
24
+
25
+ canvas = map.to_image
26
+ drawing = Magick::Draw.new
27
+
28
+ marker = Smappy::Marker.new(map.center.latitude, map.center.longitude)
29
+ position = marker.position_on_map(map)
30
+
31
+ drawing.composite(position[0], position[1], marker.width, marker.height, marker.marker_image)
32
+ drawing.draw(canvas)
33
+
34
+ canvas.write 'tmp/map.png'
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ module Smappy
2
+ VERSION = '0.0.1'
3
+
4
+ autoload :Location, 'smappy/location'
5
+ autoload :Marker, 'smappy/marker'
6
+ autoload :StaticMap, 'smappy/static_map'
7
+ autoload :Tile, 'smappy/tile'
8
+ end
@@ -0,0 +1,58 @@
1
+ module Smappy
2
+ class Location
3
+ attr_accessor :latitude, :longitude
4
+
5
+ def initialize(latitude, longitude)
6
+ self.latitude = latitude
7
+ self.longitude = longitude
8
+ end
9
+
10
+ def to_tile(options = {})
11
+ tile = Tile.new options
12
+ tile.x = tile_x_at_zoomlevel(tile.zoomlevel)
13
+ tile.y = tile_y_at_zoomlevel(tile.zoomlevel)
14
+ tile
15
+ end
16
+
17
+ def position_on_map(map)
18
+ tile = to_tile(zoomlevel: map.zoomlevel)
19
+ x1, y1 = position_on_tile(tile)
20
+ x2, y2 = tile.position_on_map(map)
21
+
22
+ [x1 + x2, y1 + y2]
23
+ end
24
+
25
+ def position_on_tile(tile)
26
+ x = tile_x_at_zoomlevel(tile.zoomlevel)
27
+ y = tile_y_at_zoomlevel(tile.zoomlevel)
28
+
29
+ [x,y].map do |position|
30
+ ((position - position.to_i) * Tile::SIZE).to_i
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def tile_y_at_zoomlevel(zoomlevel)
37
+ (
38
+ (
39
+ 1 - Math.log(
40
+ Math.tan(latitude * Math::PI / 180) +
41
+ 1 / Math.cos(latitude * Math::PI / 180)
42
+ ) / Math::PI
43
+ ) / 2 * 2 ** zoomlevel
44
+ )
45
+ end
46
+
47
+ def tile_x_at_zoomlevel(zoomlevel)
48
+ (
49
+ (
50
+ (longitude + 180) / 360
51
+ ) * (
52
+ 2 ** zoomlevel
53
+ )
54
+ )
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,39 @@
1
+ require 'open-uri'
2
+ require 'rmagick'
3
+
4
+ module Smappy
5
+ class Marker < Location
6
+ DEFAULT_IMAGE = File.expand_path('../../../markers/information_marker.png', __FILE__)
7
+
8
+ attr_accessor :image, :offset
9
+
10
+ def initialize(latitude, longitude)
11
+ @image = DEFAULT_IMAGE
12
+ @offset = [-(width / 2), -height].map(&:to_i)
13
+
14
+ super
15
+ end
16
+
17
+ def width
18
+ marker_image.columns
19
+ end
20
+
21
+ def height
22
+ marker_image.rows
23
+ end
24
+
25
+ def position_on_map(map)
26
+ x, y = super
27
+
28
+ [x + offset[0], y + offset[1]]
29
+ end
30
+
31
+ # UNTESTED:
32
+ def marker_image
33
+ @marker_image ||= (
34
+ data = open(image).read
35
+ Magick::Image.from_blob(data).first
36
+ )
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,83 @@
1
+ require 'rmagick'
2
+
3
+ module Smappy
4
+ class StaticMap
5
+ DEFAULT_OPTIONS = {
6
+ width: 500,
7
+ height: 350,
8
+ zoomlevel: 0,
9
+ center: [0.0, 0.0]
10
+ }
11
+
12
+ attr_accessor :width, :height, :zoomlevel, :center, :tile_url_template
13
+
14
+ def initialize(options = {})
15
+ options = DEFAULT_OPTIONS.merge(options)
16
+ options.each do |key, value|
17
+ send("#{key}=", value) if respond_to?("#{key}=")
18
+ end
19
+ end
20
+
21
+ def center=(new_value)
22
+ if new_value.is_a?(Location)
23
+ @center = new_value
24
+ else
25
+ @center = Location.new(new_value[0], new_value[1])
26
+ end
27
+ end
28
+
29
+ def center_tile
30
+ center.to_tile zoomlevel: zoomlevel
31
+ end
32
+
33
+ def tile_offset
34
+ center.position_on_tile(center_tile).map do |position|
35
+ position - (Tile::SIZE / 2)
36
+ end
37
+ end
38
+
39
+ def tile_options
40
+ {
41
+ zoomlevel: zoomlevel,
42
+ url_template: tile_url_template
43
+ }.delete_if do |key, value|
44
+ value.nil?
45
+ end
46
+ end
47
+
48
+ def tiles
49
+ # How many tiles do we need to fill the image:
50
+ tile_width = (width / Tile::SIZE).to_i + 1
51
+ tile_height = (height / Tile::SIZE).to_i + 1
52
+
53
+ # What are our first and last tiles:
54
+ tile_x_start = (center_tile.x - (tile_width / 2)).to_i
55
+ tile_x_end = tile_x_start + tile_width
56
+ tile_y_start = (center_tile.y - (tile_height / 2)).to_i
57
+ tile_y_end = tile_y_start + tile_height
58
+
59
+ (tile_x_start..tile_x_end).map do |x|
60
+ (tile_y_start..tile_y_end).map do |y|
61
+ Tile.new(tile_options.merge(x: x, y: y))
62
+ end
63
+ end.flatten.reject do |tile|
64
+ x,y = tile.position_on_map(self)
65
+ (x + Tile::SIZE < 0 || x > width) || (y + Tile::SIZE < 0 || y > height)
66
+ end
67
+ end
68
+
69
+ def to_image
70
+ canvas = Magick::Image.new(width, height)
71
+ drawing = Magick::Draw.new
72
+
73
+ tiles.each do |tile|
74
+ position = tile.position_on_map(self)
75
+ drawing.composite(position[0], position[1], Tile::SIZE, Tile::SIZE, tile.to_image)
76
+ end
77
+
78
+ drawing.draw(canvas)
79
+
80
+ canvas
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,52 @@
1
+ require 'open-uri'
2
+ require 'rmagick'
3
+
4
+ module Smappy
5
+ class Tile
6
+ SIZE = 256
7
+
8
+ DEFAULT_OPTIONS = {
9
+ x: 0,
10
+ y: 0,
11
+ zoomlevel: 0,
12
+ url_template: 'http://tile.openstreetmap.org/%{zoomlevel}/%{x}/%{y}.png'
13
+ }.freeze
14
+
15
+ attr_accessor :zoomlevel, :x, :y, :url_template
16
+
17
+ def initialize(options = {})
18
+ options = DEFAULT_OPTIONS.merge(options)
19
+ options.each do |key, value|
20
+ send("#{key}=", value) if respond_to?("#{key}=")
21
+ end
22
+ end
23
+
24
+ def position_on_map(map)
25
+ center_tile_x = (map.width / 2) - (Tile::SIZE / 2) - map.tile_offset[0]
26
+ center_tile_y = (map.height / 2) - (Tile::SIZE / 2) - map.tile_offset[1]
27
+
28
+ offset_x = (map.center_tile.x - x) * Tile::SIZE
29
+ offset_y = (map.center_tile.y - y) * Tile::SIZE
30
+
31
+ [center_tile_x - offset_x,
32
+ center_tile_y - offset_y]
33
+ end
34
+
35
+ def to_image
36
+ data = open(to_url).read
37
+ Magick::Image.from_blob(data).first
38
+ end
39
+
40
+ def x
41
+ @x.to_i
42
+ end
43
+
44
+ def y
45
+ @y.to_i
46
+ end
47
+
48
+ def to_url
49
+ url_template % { zoomlevel: zoomlevel, x: x, y: y }
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smappy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tom-Eric Gerritsen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rmagick
16
+ requirement: &70211739392100 !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: *70211739392100
25
+ description: Generate static maps of map tiles.
26
+ email:
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - lib/smappy/location.rb
32
+ - lib/smappy/marker.rb
33
+ - lib/smappy/static_map.rb
34
+ - lib/smappy/tile.rb
35
+ - lib/smappy.rb
36
+ - LICENSE
37
+ - Rakefile
38
+ - Gemfile
39
+ - README.md
40
+ homepage:
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.10
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Generate static maps.
64
+ test_files: []