tileup 0.1.3 → 0.1.4
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/CHANGES.md +34 -0
- data/LICENCE.txt +22 -0
- data/README.md +73 -0
- data/bin/tileup +13 -9
- data/lib/tileup.rb +2 -151
- data/lib/tileup/logger.rb +96 -0
- data/lib/tileup/tiler.rb +160 -0
- data/lib/tileup/version.rb +3 -0
- data/tileup.gemspec +24 -0
- metadata +45 -4
data/CHANGES.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Tileup Change Log
|
2
|
+
=================
|
3
|
+
|
4
|
+
v0.1.4
|
5
|
+
------
|
6
|
+
|
7
|
+
- Fix: Background color for extended tiles (now transparent instead of white,
|
8
|
+
which would clobber transparent backgrounds)
|
9
|
+
|
10
|
+
v0.1.3
|
11
|
+
------
|
12
|
+
|
13
|
+
- New: Functionality to automatically pad tiles that are not tile_width x tile_height
|
14
|
+
- New: CLI switch to disable functionality (`dont-extend-incomplete-tiles`)
|
15
|
+
|
16
|
+
v0.1.2
|
17
|
+
------
|
18
|
+
|
19
|
+
- Lost to history
|
20
|
+
|
21
|
+
v0.1.1
|
22
|
+
------
|
23
|
+
|
24
|
+
- Lost to history
|
25
|
+
|
26
|
+
v0.1.0
|
27
|
+
------
|
28
|
+
|
29
|
+
- Lost to history
|
30
|
+
|
31
|
+
v0.0.1
|
32
|
+
------
|
33
|
+
|
34
|
+
- Lost to history
|
data/LICENCE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Oliver Marriott
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
Tile Up
|
2
|
+
=======
|
3
|
+
|
4
|
+
*Tile Up* is a ruby Ruby gem that splits a large image into a set of tiles to use with javascript mapping libraries such as [Leaflet.js](http://leafletjs.com) or [Modest Maps](http://modestmaps.com/). You can also use *Tile Up* to make tiles for `CATiledLayer` (or anything else really...).
|
5
|
+
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
`gem install tileup`
|
10
|
+
|
11
|
+
`tileup` requires `rmagick` for image manipulation, which depends on `imagemagick`. `imagemagick` is avaliable through `homebrew`.
|
12
|
+
|
13
|
+
Usage
|
14
|
+
-----
|
15
|
+
|
16
|
+
### Basics
|
17
|
+
|
18
|
+
To generate some tiles from a large image, you'll probably use something like:
|
19
|
+
|
20
|
+
```
|
21
|
+
tileup --in huge_image.png --output-dir image_tiles \
|
22
|
+
--prefix my_tiles --verbose
|
23
|
+
```
|
24
|
+
|
25
|
+
Which will split `huge_image.png` up into `256x256` (default) sized tiles, and save them into the directory `image_tiles`. The images will be saved as `my_tiles_[COLUMN]_[ROW].png`
|
26
|
+
|
27
|
+
```
|
28
|
+
image_tiles/my_tiles_0_0.png
|
29
|
+
image_tiles/my_tiles_0_1.png
|
30
|
+
image_tiles/my_tiles_0_2.png
|
31
|
+
...
|
32
|
+
```
|
33
|
+
|
34
|
+
### Auto zooming
|
35
|
+
|
36
|
+
`tileup` can also scale your image for a number of zoom levels (max 20 levels). This is done by *scaling down* the original image, so make sure its pretty big.
|
37
|
+
|
38
|
+
*(Auto zooming is an experimental hack, it should work fine for smaller increments, but may be unreliable at higher levels. E.g. `--auto-zoom 4` should work fine, `--auto-zoom 20` might not work so well.)*
|
39
|
+
|
40
|
+
```
|
41
|
+
tileup --in really_huge_image.png --auto-zoom 4 \
|
42
|
+
--output-dir map_tiles
|
43
|
+
```
|
44
|
+
|
45
|
+
`--auto-zoom 4` means, make 4 levesl of zoom, starting from `really_huge_image.png` at zoom level 20, then scale that down for 19, etc.
|
46
|
+
|
47
|
+
You should see something like:
|
48
|
+
|
49
|
+
```
|
50
|
+
map_tiles/20/map_tile_0_0.png
|
51
|
+
map_tiles/20/map_tile_0_1.png
|
52
|
+
map_tiles/20/map_tile_0_2.png
|
53
|
+
...
|
54
|
+
map_tiles/19/map_tile_0_0.png
|
55
|
+
map_tiles/19/map_tile_0_1.png
|
56
|
+
map_tiles/19/map_tile_0_2.png
|
57
|
+
...
|
58
|
+
```
|
59
|
+
*(where `20` is zoom level 20, the largest zoom, `19` is half the size of `20`, `18` is half the size of `19`, …)*
|
60
|
+
|
61
|
+
|
62
|
+
### Getting help
|
63
|
+
|
64
|
+
You can get help by running `tileup -h`.
|
65
|
+
|
66
|
+
Contributing
|
67
|
+
------------
|
68
|
+
|
69
|
+
Fixes and patches welcome, to contribute:
|
70
|
+
|
71
|
+
1. Fork this project
|
72
|
+
1. Create a feature or fix branch *off the develop branch*
|
73
|
+
1. Submit a pull request on that branch
|
data/bin/tileup
CHANGED
@@ -3,12 +3,6 @@
|
|
3
3
|
require 'tileup'
|
4
4
|
require 'optparse'
|
5
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
6
|
options = {
|
13
7
|
tile_width: 256,
|
14
8
|
tile_height: 256,
|
@@ -20,19 +14,24 @@ options = {
|
|
20
14
|
}
|
21
15
|
|
22
16
|
OptionParser.new do |o|
|
23
|
-
o.on('--in=input_file', "
|
17
|
+
o.on('--in=input_file', "Required input file, your large image to tile up.") { |input_filename| options[:input_filename] = input_filename }
|
24
18
|
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
19
|
o.on('--tile-width=256', "Tile width, should normally equal tile height.") { |width| options[:tile_width] = width }
|
26
20
|
o.on('--tile-height=256', "Tile height, should normally equal tile width.") { |height| options[:tile_height] = height }
|
27
21
|
o.on('--auto-zoom=', Integer, "Automatically scale input image N times, must be a number."){ |zooms| options[:auto_zoom_levels] = zooms }
|
28
22
|
o.on('--output-dir=', "Output directory (will be created if it doesn't exist).") { |output_dir| options[:output_dir] = output_dir }
|
29
23
|
o.on('--dont-extend-incomplete-tiles',
|
30
|
-
"Do not extend edge tiles if they do not fill an entire tile_width x tile_height.
|
24
|
+
"Do not extend edge tiles if they do not fill an entire tile_width x tile_height. " +
|
25
|
+
"By default tileup will extend tiles to tile_width x tile_height if required.") {|e|options[:extend_incomplete_tiles] = false}
|
31
26
|
o.on('-v', '--verbose', "Enable verbose logging.") { |verbose| options[:verbose] = true }
|
32
27
|
o.on('-h', '--help', "You're looking at it.") { puts o; exit }
|
28
|
+
o.on('--version', "Version information (v#{TileUp::VERSION})") { puts TileUp::VERSION; exit }
|
33
29
|
begin
|
34
30
|
o.parse!
|
31
|
+
rescue SystemExit => e
|
32
|
+
exit
|
35
33
|
rescue Exception => e
|
34
|
+
puts e.class
|
36
35
|
puts "Argument error, #{e.message}. Try running '#{File.basename($PROGRAM_NAME)} -h'"
|
37
36
|
exit
|
38
37
|
end
|
@@ -43,4 +42,9 @@ if options[:input_filename].nil?
|
|
43
42
|
exit 1
|
44
43
|
end
|
45
44
|
|
46
|
-
|
45
|
+
begin
|
46
|
+
TileUp::Tiler.new(options[:input_filename], options)
|
47
|
+
rescue Interrupt
|
48
|
+
puts "\n\nInterrupt received, exiting.\n"
|
49
|
+
exit
|
50
|
+
end
|
data/lib/tileup.rb
CHANGED
@@ -1,151 +1,2 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'fileutils'
|
4
|
-
|
5
|
-
module TileUp
|
6
|
-
|
7
|
-
class Tiler
|
8
|
-
|
9
|
-
def initialize(image_filename, options)
|
10
|
-
default_options = {
|
11
|
-
auto_zoom_levels: nil,
|
12
|
-
tile_width: 256,
|
13
|
-
tile_height: 256,
|
14
|
-
filename_prefix: "map_tile",
|
15
|
-
output_dir: ".",
|
16
|
-
extend_incomplete_tiles: true,
|
17
|
-
verbose: false
|
18
|
-
}
|
19
|
-
@options = OpenStruct.new(default_options.merge(options))
|
20
|
-
@options.tile_width = @options.tile_width.to_i unless !@options.tile_width.respond_to? :to_i
|
21
|
-
@options.tile_height = @options.tile_height.to_i unless !@options.tile_height.respond_to? :to_i
|
22
|
-
@extension = image_filename.split(".").last
|
23
|
-
@filename_prefix = @options.filename_prefix
|
24
|
-
|
25
|
-
begin
|
26
|
-
@image = Magick::Image::read(image_filename).first
|
27
|
-
rescue Magick::ImageMagickError => e
|
28
|
-
puts "Could not open image #{image_filename}."
|
29
|
-
exit
|
30
|
-
end
|
31
|
-
|
32
|
-
if @options.auto_zoom_levels && @options.auto_zoom_levels > 20
|
33
|
-
puts "Warning: auto zoom levels hard limited to 20."
|
34
|
-
@options.auto_zoom_levels = 20
|
35
|
-
end
|
36
|
-
if @options.auto_zoom_levels && @options.auto_zoom_levels <= 0
|
37
|
-
@options.auto_zoom_levels = nil
|
38
|
-
end
|
39
|
-
|
40
|
-
puts "Opened #{image_filename}, #{@image.columns} x #{@image.rows}"
|
41
|
-
|
42
|
-
# pre-process our inputs to work out what we're supposed to do
|
43
|
-
tasks = []
|
44
|
-
|
45
|
-
if @options.auto_zoom_levels.nil?
|
46
|
-
# if we have no auto zoom request, then
|
47
|
-
# we dont shrink or scale, and save directly to the output
|
48
|
-
# dir.
|
49
|
-
tasks << {
|
50
|
-
output_dir: @options.output_dir, # normal output dir
|
51
|
-
scale: 1.0 # dont scale
|
52
|
-
}
|
53
|
-
else
|
54
|
-
# do have zoom levels, so construct those tasks
|
55
|
-
zoom_name = 20
|
56
|
-
scale = 1.0
|
57
|
-
tasks << {
|
58
|
-
output_dir: File.join(@options.output_dir, zoom_name.to_s),
|
59
|
-
scale: scale
|
60
|
-
}
|
61
|
-
(@options.auto_zoom_levels-1).times do |level|
|
62
|
-
scale = scale / 2.0
|
63
|
-
zoom_name = zoom_name - 1
|
64
|
-
tasks << {
|
65
|
-
output_dir: File.join(@options.output_dir, zoom_name.to_s),
|
66
|
-
scale: scale
|
67
|
-
}
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# run through tasks list
|
72
|
-
tasks.each do |task|
|
73
|
-
image = @image
|
74
|
-
image_path = File.join(task[:output_dir], @filename_prefix)
|
75
|
-
if task[:scale] != 1.0
|
76
|
-
# if scale required, scale image
|
77
|
-
begin
|
78
|
-
image = @image.scale(task[:scale])
|
79
|
-
rescue RuntimeError => e
|
80
|
-
message = "Failed to scale image, are you sure the original image "\
|
81
|
-
"is large enough to scale down this far (#{scale}) with this "\
|
82
|
-
"tilesize (#{@options.tile_width}x#{@options.tile_height})?"
|
83
|
-
puts message
|
84
|
-
exit
|
85
|
-
end
|
86
|
-
end
|
87
|
-
# make output dir
|
88
|
-
make_path(task[:output_dir])
|
89
|
-
self.make_tiles(image, image_path, @options.tile_width, @options.tile_height)
|
90
|
-
image = nil
|
91
|
-
end
|
92
|
-
|
93
|
-
puts "Finished."
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
def make_path(directory_path)
|
98
|
-
FileUtils.mkdir_p directory_path
|
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.to_f).ceil
|
106
|
-
num_rows = (image.rows/tile_height.to_f).ceil
|
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
|
-
|
134
|
-
# unless told to do otherwise, extend tiles in the last row and column
|
135
|
-
# if they do not fill an entire tile width and height.
|
136
|
-
if @options.extend_incomplete_tiles && (c[:row] == num_rows - 1 || c[:column] == num_columns - 1)
|
137
|
-
# fill to width height, start from top left corner.
|
138
|
-
ci = ci.extent(tile_width, tile_height, 0, 0)
|
139
|
-
end
|
140
|
-
|
141
|
-
print "Saving tile: #{c[:row]}, #{c[:column]}..." if @options.verbose
|
142
|
-
ci.write("#{filename_prefix}_#{c[:column]}_#{c[:row]}.#{@extension}")
|
143
|
-
print "\rSaving tile: #{c[:row]}, #{c[:column]}... saved\n" if @options.verbose
|
144
|
-
|
145
|
-
ci = nil
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
|
151
|
-
end
|
1
|
+
require 'tileup/tiler'
|
2
|
+
require 'tileup/version'
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module TileUp
|
5
|
+
|
6
|
+
# Base logger class, subclass this, do not use directly.
|
7
|
+
class TileUpLogger
|
8
|
+
|
9
|
+
def self.sym_to_severity(sym)
|
10
|
+
severities = {
|
11
|
+
:debug => Logger::DEBUG,
|
12
|
+
:info => Logger::INFO,
|
13
|
+
:warn => Logger::WARN,
|
14
|
+
:error => Logger::ERROR,
|
15
|
+
:fatal => Logger::FATAL
|
16
|
+
}
|
17
|
+
severity = severities[sym] || Logger::UNKNOWN
|
18
|
+
end
|
19
|
+
|
20
|
+
# create logger set to given level
|
21
|
+
# where level is a symbol (:debug, :info, :warn, :error, :fatal)
|
22
|
+
# options may specifiy verbose, which will log more info messages
|
23
|
+
def initialize(level, options)
|
24
|
+
@severity = level
|
25
|
+
default_options = {
|
26
|
+
verbose: false
|
27
|
+
}
|
28
|
+
@options = OpenStruct.new(default_options.merge(options))
|
29
|
+
end
|
30
|
+
|
31
|
+
def level
|
32
|
+
@level
|
33
|
+
end
|
34
|
+
|
35
|
+
def level=(severity)
|
36
|
+
logger.level = TileUpLogger.sym_to_severity(severity)
|
37
|
+
end
|
38
|
+
|
39
|
+
# log an error message
|
40
|
+
def error(message)
|
41
|
+
# note, we always log error messages
|
42
|
+
add(:error, message)
|
43
|
+
end
|
44
|
+
|
45
|
+
# log a regular message
|
46
|
+
def info(message)
|
47
|
+
add(:info, message)
|
48
|
+
end
|
49
|
+
|
50
|
+
def warn(message)
|
51
|
+
add(:warn, message)
|
52
|
+
end
|
53
|
+
|
54
|
+
# log a verbose message
|
55
|
+
def verbose(message)
|
56
|
+
add(:info, message) if verbose?
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# add message to log
|
62
|
+
def add(severity, message)
|
63
|
+
severity = TileUpLogger.sym_to_severity(severity)
|
64
|
+
logger.add(severity, message)
|
65
|
+
end
|
66
|
+
|
67
|
+
# is logger in verbose mode?
|
68
|
+
def verbose?
|
69
|
+
@options.verbose
|
70
|
+
end
|
71
|
+
|
72
|
+
# create or return a logger
|
73
|
+
def logger
|
74
|
+
@logger ||= create_logger
|
75
|
+
end
|
76
|
+
|
77
|
+
# subclasses should overwrite this method, creating what ever
|
78
|
+
# logger they want to
|
79
|
+
def create_logger
|
80
|
+
raise "You should create your own `create_logger` method"
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# Log to console logger
|
86
|
+
class ConsoleLogger < TileUpLogger
|
87
|
+
private
|
88
|
+
def create_logger
|
89
|
+
@logger = Logger.new(STDOUT)
|
90
|
+
@logger.formatter = Proc.new do |sev, time, prg, msg|
|
91
|
+
"#{time.strftime('%H:%M:%S').to_s} => #{msg}\n"
|
92
|
+
end
|
93
|
+
@logger
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/tileup/tiler.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'RMagick'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tileup/logger'
|
5
|
+
|
6
|
+
module TileUp
|
7
|
+
|
8
|
+
class Tiler
|
9
|
+
|
10
|
+
def initialize(image_filename, options)
|
11
|
+
default_options = {
|
12
|
+
auto_zoom_levels: nil,
|
13
|
+
tile_width: 256,
|
14
|
+
tile_height: 256,
|
15
|
+
filename_prefix: "map_tile",
|
16
|
+
output_dir: ".",
|
17
|
+
extend_incomplete_tiles: true,
|
18
|
+
verbose: false
|
19
|
+
}
|
20
|
+
@options = OpenStruct.new(default_options.merge(options))
|
21
|
+
@options.tile_width = @options.tile_width.to_i unless !@options.tile_width.respond_to? :to_i
|
22
|
+
@options.tile_height = @options.tile_height.to_i unless !@options.tile_height.respond_to? :to_i
|
23
|
+
@extension = image_filename.split(".").last
|
24
|
+
@filename_prefix = @options.filename_prefix
|
25
|
+
@logger = ConsoleLogger.new(:info, {verbose: @options.verbose})
|
26
|
+
|
27
|
+
begin
|
28
|
+
@image = Magick::Image::read(image_filename).first
|
29
|
+
rescue Magick::ImageMagickError => e
|
30
|
+
@logger.error "Could not open image #{image_filename}."
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
|
34
|
+
if @options.auto_zoom_levels && @options.auto_zoom_levels > 20
|
35
|
+
@logger.warn "Warning: auto zoom levels hard limited to 20."
|
36
|
+
@options.auto_zoom_levels = 20
|
37
|
+
end
|
38
|
+
if @options.auto_zoom_levels && @options.auto_zoom_levels <= 0
|
39
|
+
@options.auto_zoom_levels = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
@logger.info "Opened #{image_filename}, #{@image.columns} x #{@image.rows}"
|
43
|
+
|
44
|
+
# pre-process our inputs to work out what we're supposed to do
|
45
|
+
tasks = []
|
46
|
+
|
47
|
+
if @options.auto_zoom_levels.nil?
|
48
|
+
# if we have no auto zoom request, then
|
49
|
+
# we dont shrink or scale, and save directly to the output
|
50
|
+
# dir.
|
51
|
+
tasks << {
|
52
|
+
output_dir: @options.output_dir, # normal output dir
|
53
|
+
scale: 1.0 # dont scale
|
54
|
+
}
|
55
|
+
else
|
56
|
+
# do have zoom levels, so construct those tasks
|
57
|
+
zoom_name = 20
|
58
|
+
scale = 1.0
|
59
|
+
tasks << {
|
60
|
+
output_dir: File.join(@options.output_dir, zoom_name.to_s),
|
61
|
+
scale: scale
|
62
|
+
}
|
63
|
+
(@options.auto_zoom_levels-1).times do |level|
|
64
|
+
scale = scale / 2.0
|
65
|
+
zoom_name = zoom_name - 1
|
66
|
+
tasks << {
|
67
|
+
output_dir: File.join(@options.output_dir, zoom_name.to_s),
|
68
|
+
scale: scale
|
69
|
+
}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# run through tasks list
|
74
|
+
tasks.each do |task|
|
75
|
+
image = @image
|
76
|
+
image_path = File.join(task[:output_dir], @filename_prefix)
|
77
|
+
if task[:scale] != 1.0
|
78
|
+
# if scale required, scale image
|
79
|
+
begin
|
80
|
+
image = @image.scale(task[:scale])
|
81
|
+
rescue RuntimeError => e
|
82
|
+
message = "Failed to scale image, are you sure the original image "\
|
83
|
+
"is large enough to scale down this far (#{scale}) with this "\
|
84
|
+
"tilesize (#{@options.tile_width}x#{@options.tile_height})?"
|
85
|
+
@logger.error message
|
86
|
+
exit
|
87
|
+
end
|
88
|
+
end
|
89
|
+
# make output dir
|
90
|
+
make_path(task[:output_dir])
|
91
|
+
self.make_tiles(image, image_path, @options.tile_width, @options.tile_height)
|
92
|
+
image = nil
|
93
|
+
end
|
94
|
+
|
95
|
+
@logger.info "Finished."
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
def make_path(directory_path)
|
100
|
+
FileUtils.mkdir_p directory_path
|
101
|
+
end
|
102
|
+
|
103
|
+
def make_tiles(image, filename_prefix, tile_width, tile_height)
|
104
|
+
# find image width and height
|
105
|
+
# then find out how many tiles we'll get out of
|
106
|
+
# the image, then use that for the xy offset in crop.
|
107
|
+
num_columns = (image.columns/tile_width.to_f).ceil
|
108
|
+
num_rows = (image.rows/tile_height.to_f).ceil
|
109
|
+
x,y,column,row = 0,0,0,0
|
110
|
+
crops = []
|
111
|
+
|
112
|
+
@logger.info "Tiling image into columns: #{num_columns}, rows: #{num_rows}"
|
113
|
+
|
114
|
+
while true
|
115
|
+
x = column * tile_width
|
116
|
+
y = row * tile_height
|
117
|
+
crops << {
|
118
|
+
x: x,
|
119
|
+
y: y,
|
120
|
+
row: row,
|
121
|
+
column: column
|
122
|
+
}
|
123
|
+
column = column + 1
|
124
|
+
if column >= num_columns
|
125
|
+
column = 0
|
126
|
+
row = row + 1
|
127
|
+
end
|
128
|
+
if row >= num_rows
|
129
|
+
break
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
crops.each do |c|
|
134
|
+
@logger.verbose "Crop: x: #{c[:x]}, y: #{c[:y]}, w: #{tile_width}, h: #{tile_height}"
|
135
|
+
ci = image.crop(c[:x], c[:y], tile_width, tile_height, true);
|
136
|
+
|
137
|
+
# unless told to do otherwise, extend tiles in the last row and column
|
138
|
+
# if they do not fill an entire tile width and height.
|
139
|
+
|
140
|
+
is_edge = (c[:row] == num_rows - 1 || c[:column] == num_columns - 1)
|
141
|
+
needs_extension = ci.rows != tile_height || ci.columns != tile_width
|
142
|
+
if @options.extend_incomplete_tiles && is_edge && needs_extension
|
143
|
+
# defaults to white background color, but transparent is probably
|
144
|
+
# a better default for our purposes.
|
145
|
+
ci.background_color = "none"
|
146
|
+
# fill to width height, start from top left corner.
|
147
|
+
ci = ci.extent(tile_width, tile_height, 0, 0)
|
148
|
+
end
|
149
|
+
|
150
|
+
@logger.verbose "Saving tile: #{c[:row]}, #{c[:column]}..."
|
151
|
+
ci.write("#{filename_prefix}_#{c[:column]}_#{c[:row]}.#{@extension}")
|
152
|
+
@logger.verbose "Saving tile: #{c[:row]}, #{c[:column]}... saved"
|
153
|
+
|
154
|
+
ci = nil
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
data/tileup.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tileup/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'tileup'
|
8
|
+
spec.version = TileUp::VERSION
|
9
|
+
spec.date = Date.today.to_s
|
10
|
+
spec.summary = "Turn an image into an X,Y tile set for use with JS mapping libraries"
|
11
|
+
spec.description = spec.summary
|
12
|
+
spec.authors = ["Oliver Marriott"]
|
13
|
+
spec.email = 'hello@omarriott.com'
|
14
|
+
spec.homepage = 'http://github.com/rktjmp/tileup'
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.add_runtime_dependency "rmagick", ["~> 2.13.2"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tileup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rmagick
|
@@ -27,6 +27,38 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 2.13.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: bundler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.3'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.3'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
30
62
|
description: Turn an image into an X,Y tile set for use with JS mapping libraries
|
31
63
|
email: hello@omarriott.com
|
32
64
|
executables:
|
@@ -34,10 +66,18 @@ executables:
|
|
34
66
|
extensions: []
|
35
67
|
extra_rdoc_files: []
|
36
68
|
files:
|
37
|
-
-
|
69
|
+
- CHANGES.md
|
70
|
+
- LICENCE.txt
|
71
|
+
- README.md
|
38
72
|
- bin/tileup
|
73
|
+
- lib/tileup.rb
|
74
|
+
- lib/tileup/logger.rb
|
75
|
+
- lib/tileup/tiler.rb
|
76
|
+
- lib/tileup/version.rb
|
77
|
+
- tileup.gemspec
|
39
78
|
homepage: http://github.com/rktjmp/tileup
|
40
|
-
licenses:
|
79
|
+
licenses:
|
80
|
+
- MIT
|
41
81
|
post_install_message:
|
42
82
|
rdoc_options: []
|
43
83
|
require_paths:
|
@@ -61,3 +101,4 @@ signing_key:
|
|
61
101
|
specification_version: 3
|
62
102
|
summary: Turn an image into an X,Y tile set for use with JS mapping libraries
|
63
103
|
test_files: []
|
104
|
+
has_rdoc:
|