tileup 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/bin/tileup +44 -0
  2. data/lib/tileup.rb +142 -0
  3. metadata +63 -0
data/bin/tileup ADDED
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tileup'
4
+ require 'optparse'
5
+
6
+ # auto zoom bigmap.png down 4 times, output to map-tiles dir with map-tile-X-Y.png filename
7
+ # maw --in "bigmap.png" --auto-zoom --auto-zoom-levels 4 --output-dir "map-tiles" --filename-prefx="map-tile"
8
+
9
+ # maw --in "bigmap.png" --output-dir "map-tiles/20/" --filename-prefix="map-tile" --tile-width 256 --tile-height 256
10
+
11
+
12
+ options = {
13
+ tile_width: 256,
14
+ tile_height: 256,
15
+ filename_prefix: "map_tile",
16
+ auto_zoom_levels: nil,
17
+ input_filename: nil,
18
+ output_dir: '.'
19
+
20
+ }
21
+
22
+ OptionParser.new do |o|
23
+ o.on('--in=input_file', " Required input file, your large image to tile up") { |input_filename| options[:input_filename] = input_filename }
24
+ o.on('--prefix=map_tile', "Prefix to append to tile files, e.g. --prefix=my_tile => my_tile_[XN]_[YN].png") { |prefix| options[:filename_prefix] = prefix }
25
+ o.on('--tile-width=256', "Tile width, should normally equal tile height") { |width| options[:tile_width] = width }
26
+ o.on('--tile-height=256', "Tile height, should normally equal tile width") { |height| options[:tile_height] = height }
27
+ o.on('--auto-zoom=', Integer, "Automatically scale input image N times, must be a number"){ |zooms| options[:auto_zoom_levels] = zooms }
28
+ o.on('--output-dir=', "Output directory (will be created if it doesn't exist)") { |output_dir| options[:output_dir] = output_dir }
29
+ o.on('-v', '--verbose', "Enable verbose logging") { |verbose| options[:verbose] = true }
30
+ o.on('-h', '--help', "You're looking at it.") { puts o; exit }
31
+ begin
32
+ o.parse!
33
+ rescue Exception => e
34
+ puts "Argument error, #{e.message}. Try running '#{File.basename($PROGRAM_NAME)} -h'"
35
+ exit
36
+ end
37
+ end
38
+
39
+ if options[:input_filename].nil?
40
+ puts "No input file specified, Try running '#{File.basename($PROGRAM_NAME)} -h'"
41
+ exit 1
42
+ end
43
+
44
+ TileUp::Tiler.new(options[:input_filename], options)
data/lib/tileup.rb ADDED
@@ -0,0 +1,142 @@
1
+ require 'ostruct'
2
+ require 'rmagick'
3
+
4
+ module TileUp
5
+
6
+ class Tiler
7
+
8
+ def initialize(image_filename, options)
9
+ default_options = {
10
+ auto_zoom_levels: nil,
11
+ tile_width: 256,
12
+ tile_height: 256,
13
+ filename_prefix: "map_tile",
14
+ output_dir: ".",
15
+ verbose: false
16
+ }
17
+ @options = OpenStruct.new(default_options.merge(options))
18
+ @extension = image_filename.split(".").last
19
+ @filename_prefix = @options.filename_prefix
20
+
21
+ begin
22
+ @image = Magick::Image::read(image_filename).first
23
+ rescue Magick::ImageMagickError => e
24
+ puts "Could not open image #{image_filename}."
25
+ exit
26
+ end
27
+
28
+ if @options.auto_zoom_levels && @options.auto_zoom_levels > 20
29
+ puts "Warning: auto zoom levels hard limited to 20."
30
+ @options.auto_zoom_levels = 20
31
+ end
32
+ if @options.auto_zoom_levels && @options.auto_zoom_levels <= 0
33
+ @options.auto_zoom_levels = nil
34
+ end
35
+
36
+ puts "Opened #{image_filename}, #{@image.columns} x #{@image.rows}"
37
+
38
+ # pre-process our inputs to work out what we're supposed to do
39
+ tasks = []
40
+
41
+ if @options.auto_zoom_levels.nil?
42
+ # if we have no auto zoom request, then
43
+ # we dont shrink or scale, and save directly to the output
44
+ # dir.
45
+ tasks << {
46
+ output_dir: @options.output_dir, # normal output dir
47
+ scale: 1.0 # dont scale
48
+ }
49
+ else
50
+ # do have zoom levels, so construct those tasks
51
+ zoom_name = 20
52
+ scale = 1.0
53
+ tasks << {
54
+ output_dir: File.join(@options.output_dir, zoom_name.to_s),
55
+ scale: scale
56
+ }
57
+ (@options.auto_zoom_levels-1).times do |level|
58
+ scale = scale / 2.0
59
+ zoom_name = zoom_name - 1
60
+ tasks << {
61
+ output_dir: File.join(@options.output_dir, zoom_name.to_s),
62
+ scale: scale
63
+ }
64
+ end
65
+ end
66
+
67
+ # run through tasks list
68
+ tasks.each do |task|
69
+ image = @image
70
+ image_path = File.join(task[:output_dir], @filename_prefix)
71
+ if task[:scale] != 1.0
72
+ # if scale required, scale image
73
+ begin
74
+ image = @image.scale(task[:scale])
75
+ rescue RuntimeError => e
76
+ message = "Failed to scale image, are you sure the original image "\
77
+ "is large enough to scale down this far (#{scale}) with this "\
78
+ "tilesize (#{@options.tile_width}x#{@options.tile_height})?"
79
+ puts message
80
+ exit
81
+ end
82
+ end
83
+ # make output dir
84
+ make_path(task[:output_dir])
85
+ self.make_tiles(image, image_path, @options.tile_width, @options.tile_height)
86
+ image = nil
87
+ end
88
+
89
+ puts "Finished."
90
+
91
+ end
92
+
93
+ def make_path(directory_path)
94
+ parts = directory_path.split(File::SEPARATOR);
95
+ parts.each_index do |i|
96
+ upto = parts[0..i].join(File::SEPARATOR)
97
+ Dir::mkdir(upto) unless Dir::exists?(upto)
98
+ end
99
+ end
100
+
101
+ def make_tiles(image, filename_prefix, tile_width, tile_height)
102
+ # find image width and height
103
+ # then find out how many tiles we'll get out of
104
+ # the image, then use that for the xy offset in crop.
105
+ num_columns = image.columns/tile_width
106
+ num_rows = image.rows/tile_height
107
+ x,y,column,row = 0,0,0,0
108
+ crops = []
109
+
110
+ puts "Tiling image into columns: #{num_columns}, rows: #{num_rows}"
111
+
112
+ while true
113
+ x = column * tile_width
114
+ y = row * tile_height
115
+ crops << {
116
+ x: x,
117
+ y: y,
118
+ row: row,
119
+ column: column
120
+ }
121
+ column = column + 1
122
+ if column >= num_columns
123
+ column = 0
124
+ row = row + 1
125
+ end
126
+ if row >= num_rows
127
+ break
128
+ end
129
+ end
130
+
131
+ crops.each do |c|
132
+ ci = image.crop(c[:x], c[:y], tile_width, tile_height, true);
133
+ print "Saving tile: #{c[:row]}, #{c[:column]}..." if @options.verbose
134
+ ci.write("#{filename_prefix}_#{c[:column]}_#{c[:row]}.#{@extension}")
135
+ print "\rSaving tile: #{c[:row]}, #{c[:column]}... saved\n" if @options.verbose
136
+ ci = nil
137
+ end
138
+ end
139
+
140
+ end
141
+
142
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tileup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Oliver Marriott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rmagick
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.13.2
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: 2.13.2
30
+ description: Turn an image into an X,Y tile set for use with JS mapping libraries
31
+ email: hello@omarriott.com
32
+ executables:
33
+ - tileup
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/tileup.rb
38
+ - bin/tileup
39
+ homepage: http://rubygems.org/gems/tileup
40
+ licenses: []
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ! '>='
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubyforge_project:
59
+ rubygems_version: 1.8.25
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Turn an image into an X,Y tile set for use with JS mapping libraries
63
+ test_files: []