moiristo-tileup 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +12 -0
- data/CHANGES.md +43 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +34 -0
- data/LICENCE.txt +22 -0
- data/README.md +94 -0
- data/Rakefile +10 -0
- data/bin/tileup +45 -0
- data/lib/tileup.rb +10 -0
- data/lib/tileup/image_processor.rb +66 -0
- data/lib/tileup/image_processors/mini_magick.rb +62 -0
- data/lib/tileup/image_processors/rmagick.rb +57 -0
- data/lib/tileup/logger.rb +94 -0
- data/lib/tileup/loggers/console.rb +18 -0
- data/lib/tileup/loggers/none.rb +18 -0
- data/lib/tileup/tiler.rb +155 -0
- data/lib/tileup/version.rb +3 -0
- data/tileup.gemspec +32 -0
- metadata +147 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c2f73ac375c4eacafa750e4e03ef7343fa988544
|
4
|
+
data.tar.gz: c6ec4fe16e14a3c943fe5f486359df568873fbf0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f037259541831f2af80bf5cfc74ea9a66fc6e51c0b346aa2d9032d7b2dfc1c8c244b65c4d1d139c759e695e6cfa4c0a3bdf154c56a5afcdb52b760cfae833a6
|
7
|
+
data.tar.gz: 31751b7bc4d79339912bed4fc4e057005332b60f27b051009b27eda19ce5cefc7c3238e676359775813f544e859f2e867f003aca3fa5b489feb1b202c8adb1e5
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGES.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Tileup Change Log
|
2
|
+
=================
|
3
|
+
|
4
|
+
v1.0.0
|
5
|
+
------
|
6
|
+
|
7
|
+
- Added: MiniMagick support
|
8
|
+
- Added: Null logger
|
9
|
+
- Added: Test suite
|
10
|
+
- Added: Travis-ci integration
|
11
|
+
- Changed: Refactored implementation, split up functionality
|
12
|
+
|
13
|
+
v0.1.4
|
14
|
+
------
|
15
|
+
|
16
|
+
- Fixed: Background color for extended tiles (now transparent instead of white,
|
17
|
+
which would clobber transparent backgrounds)
|
18
|
+
|
19
|
+
v0.1.3
|
20
|
+
------
|
21
|
+
|
22
|
+
- Added: Functionality to automatically pad tiles that are not tile_width x tile_height
|
23
|
+
- Added: CLI switch to disable functionality (`dont-extend-incomplete-tiles`)
|
24
|
+
|
25
|
+
v0.1.2
|
26
|
+
------
|
27
|
+
|
28
|
+
- Lost to history
|
29
|
+
|
30
|
+
v0.1.1
|
31
|
+
------
|
32
|
+
|
33
|
+
- Lost to history
|
34
|
+
|
35
|
+
v0.1.0
|
36
|
+
------
|
37
|
+
|
38
|
+
- Lost to history
|
39
|
+
|
40
|
+
v0.0.1
|
41
|
+
------
|
42
|
+
|
43
|
+
- Lost to history
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
moiristo-tileup (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
coderay (1.1.0)
|
10
|
+
method_source (0.8.2)
|
11
|
+
mini_magick (4.7.0)
|
12
|
+
minitest (5.10.2)
|
13
|
+
pry (0.10.3)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.8.1)
|
16
|
+
slop (~> 3.4)
|
17
|
+
rake (10.5.0)
|
18
|
+
rmagick (2.16.0)
|
19
|
+
slop (3.6.0)
|
20
|
+
|
21
|
+
PLATFORMS
|
22
|
+
ruby
|
23
|
+
|
24
|
+
DEPENDENCIES
|
25
|
+
bundler (~> 1.13)
|
26
|
+
mini_magick (~> 4.7)
|
27
|
+
minitest (~> 5.10)
|
28
|
+
moiristo-tileup!
|
29
|
+
pry
|
30
|
+
rake (~> 10.0)
|
31
|
+
rmagick (~> 2.16)
|
32
|
+
|
33
|
+
BUNDLED WITH
|
34
|
+
1.15.3
|
data/LICENCE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2017 Reinier de Lange
|
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,94 @@
|
|
1
|
+
# Tile Up
|
2
|
+
|
3
|
+
*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...).
|
4
|
+
|
5
|
+
[![Build Status](https://travis-ci.org/moiristo/tileup.svg?branch=master)](https://travis-ci.org/moiristo/tileup)
|
6
|
+
|
7
|
+
*Note* This is not the official version of tileup. Since the offical version was poorly maintained, I decided to create my own version instead.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
`gem install moiristo-tileup`
|
12
|
+
|
13
|
+
`tileup` requires `rmagick` or `mini_magick` for image manipulation, which depends on `imagemagick`. `imagemagick` is avaliable through `homebrew`.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
### Basics
|
18
|
+
|
19
|
+
To generate some tiles from a large image, you'll probably use something like:
|
20
|
+
|
21
|
+
```
|
22
|
+
tileup --in huge_image.png --output-dir image_tiles \
|
23
|
+
--prefix my_tiles --verbose
|
24
|
+
```
|
25
|
+
|
26
|
+
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`
|
27
|
+
|
28
|
+
```
|
29
|
+
image_tiles/my_tiles_0_0.png
|
30
|
+
image_tiles/my_tiles_0_1.png
|
31
|
+
image_tiles/my_tiles_0_2.png
|
32
|
+
...
|
33
|
+
```
|
34
|
+
|
35
|
+
### Using `Tiler` directly
|
36
|
+
|
37
|
+
You can also call `Tiler` directly from your application code:
|
38
|
+
|
39
|
+
```
|
40
|
+
tiler = TileUp::Tiler.new('huge_image.png', output_dir: 'image_tiles', processor: 'mini_magick', auto_zoom_level: 'auto', logger: 'none')
|
41
|
+
|
42
|
+
# Metadata
|
43
|
+
tiler.recommended_auto_zoom_level # Yields the recommended zoom level
|
44
|
+
tiler.image_processor.width(tiler.image) # Get the image width of 'huge_image.png' using the specified image processor
|
45
|
+
tiler.image_processor.height(tiler.image) # Get the image height of 'huge_image.png' using the specified image processor
|
46
|
+
|
47
|
+
# Tile generation
|
48
|
+
tiler.make_tiles!
|
49
|
+
```
|
50
|
+
|
51
|
+
### Auto zooming
|
52
|
+
|
53
|
+
`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.
|
54
|
+
|
55
|
+
```
|
56
|
+
tileup --in really_huge_image.png --auto-zoom auto \
|
57
|
+
--output-dir map_tiles
|
58
|
+
```
|
59
|
+
|
60
|
+
`--auto-zoom auto` means that `tileup` will autoamtically try to determine the best auto zoom level. The level will be based on the image size and the tile dimensions specified.
|
61
|
+
|
62
|
+
```
|
63
|
+
tileup --in really_huge_image.png --auto-zoom 4 \
|
64
|
+
--output-dir map_tiles
|
65
|
+
```
|
66
|
+
|
67
|
+
`--auto-zoom 4` means, make 4 levels of zoom, starting from `really_huge_image.png` at zoom level 20, then scale that down for 19, etc.
|
68
|
+
|
69
|
+
You should see something like:
|
70
|
+
|
71
|
+
```
|
72
|
+
map_tiles/20/map_tile_0_0.png
|
73
|
+
map_tiles/20/map_tile_0_1.png
|
74
|
+
map_tiles/20/map_tile_0_2.png
|
75
|
+
...
|
76
|
+
map_tiles/19/map_tile_0_0.png
|
77
|
+
map_tiles/19/map_tile_0_1.png
|
78
|
+
map_tiles/19/map_tile_0_2.png
|
79
|
+
...
|
80
|
+
```
|
81
|
+
*(where `20` is zoom level 20, the largest zoom, `19` is half the size of `20`, `18` is half the size of `19`, …)*
|
82
|
+
|
83
|
+
|
84
|
+
## Getting help
|
85
|
+
|
86
|
+
You can get help by running `tileup -h`.
|
87
|
+
|
88
|
+
### Contributing
|
89
|
+
|
90
|
+
Fixes and patches welcome, to contribute:
|
91
|
+
|
92
|
+
1. Fork this project
|
93
|
+
1. Create a feature or fix branch *off the develop branch*
|
94
|
+
1. Submit a pull request on that branch
|
data/Rakefile
ADDED
data/bin/tileup
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'tileup'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
options = TileUp::Tiler::DEFAULT_OPTIONS.dup
|
7
|
+
|
8
|
+
OptionParser.new do |o|
|
9
|
+
o.on('--in=input_file', "Required input file, your large image to tile up.") { |input_filename| options[:input_filename] = input_filename }
|
10
|
+
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 }
|
11
|
+
o.on('--tile-width=256', "Tile width, should normally equal tile height.") { |width| options[:tile_width] = width }
|
12
|
+
o.on('--tile-height=256', "Tile height, should normally equal tile width.") { |height| options[:tile_height] = height }
|
13
|
+
o.on('--auto-zoom=auto', "Automatically scale input image N times when given a number. When 'auto' is specified, the auto zoom level is automatically determined"){ |zoom_level| options[:auto_zoom_level] = zoom_level }
|
14
|
+
o.on('--output-dir=', "Output directory (will be created if it doesn't exist).") { |output_dir| options[:output_dir] = output_dir }
|
15
|
+
o.on('--processor=rmagick', "Image processor, should be 'rmagick' or 'mini_magick'") { |processor| options[:processor] = processor }
|
16
|
+
o.on('--logger=console', "Sets logger, should be 'console' or 'none'") { |logger| options[:logger] = logger }
|
17
|
+
o.on('--extend-color=none', "Extend color for edge tiles.") {|extend_color| options[:extend_color] = extend_color }
|
18
|
+
o.on('--dont-extend-incomplete-tiles',
|
19
|
+
"Do not extend edge tiles if they do not fill an entire tile_width x tile_height. " +
|
20
|
+
"By default tileup will extend tiles to tile_width x tile_height if required.") {|e| options[:extend_incomplete_tiles] = false}
|
21
|
+
o.on('-v', '--verbose', "Enable verbose logging.") { |verbose| options[:verbose] = true }
|
22
|
+
o.on('-h', '--help', "You're looking at it.") { puts o; exit }
|
23
|
+
o.on('--version', "Version information (v#{TileUp::VERSION})") { puts TileUp::VERSION; exit }
|
24
|
+
begin
|
25
|
+
o.parse!
|
26
|
+
rescue SystemExit => e
|
27
|
+
exit
|
28
|
+
rescue Exception => e
|
29
|
+
puts e.class
|
30
|
+
puts "Argument error, #{e.message}. Try running '#{File.basename($PROGRAM_NAME)} -h'"
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:input_filename].nil?
|
36
|
+
puts "No input file specified, Try running '#{File.basename($PROGRAM_NAME)} -h'"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
|
40
|
+
begin
|
41
|
+
TileUp::Tiler.new(options[:input_filename], options).make_tiles!
|
42
|
+
rescue Interrupt
|
43
|
+
puts "\n\nInterrupt received, exiting.\n"
|
44
|
+
exit
|
45
|
+
end
|
data/lib/tileup.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'tileup/logger'
|
2
|
+
require 'tileup/loggers/console'
|
3
|
+
require 'tileup/loggers/none'
|
4
|
+
|
5
|
+
require 'tileup/image_processor'
|
6
|
+
require 'tileup/image_processors/mini_magick'
|
7
|
+
require 'tileup/image_processors/rmagick'
|
8
|
+
|
9
|
+
require 'tileup/tiler'
|
10
|
+
require 'tileup/version'
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module TileUp
|
2
|
+
class ImageProcessor
|
3
|
+
attr_accessor :logger
|
4
|
+
|
5
|
+
def self.build(processor, logger)
|
6
|
+
case processor
|
7
|
+
when 'mini_magick' then TileUp::ImageProcessors::MiniMagick.new(logger)
|
8
|
+
else TileUp::ImageProcessors::RMagick.new(logger)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize logger
|
13
|
+
self.logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def open image_filename
|
17
|
+
image = open_image(image_filename)
|
18
|
+
logger.info "Opened #{image_filename}, #{width(image)} x #{height(image)}"
|
19
|
+
image
|
20
|
+
rescue => e
|
21
|
+
logger.error "Could not open image #{image_filename}: #{e.message}"
|
22
|
+
raise e
|
23
|
+
end
|
24
|
+
|
25
|
+
def scale image, scale
|
26
|
+
logger.verbose "Scale: #{scale}"
|
27
|
+
|
28
|
+
if scale != 1.0
|
29
|
+
scale_image(image, scale)
|
30
|
+
else
|
31
|
+
image
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def crop_and_save image, crop, filename:, extend_crop: false, extend_color: 'none'
|
36
|
+
logger.verbose "Crop: x: #{crop[:x]}, y: #{crop[:y]}, w: #{crop[:width]}, h: #{crop[:height]}"
|
37
|
+
crop_and_save_image(image, crop, filename: filename, extend_crop: extend_crop, extend_color: extend_color)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Interface
|
41
|
+
|
42
|
+
def width image
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def height image
|
47
|
+
raise NotImplementedError
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def open_image image
|
53
|
+
raise NotImplementedError
|
54
|
+
end
|
55
|
+
|
56
|
+
def scale_image image
|
57
|
+
raise NotImplementedError
|
58
|
+
end
|
59
|
+
|
60
|
+
def crop_image image, crop, extend_crop: false, extend_color: 'none'
|
61
|
+
raise NotImplementedError
|
62
|
+
end
|
63
|
+
|
64
|
+
# / Interface
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module TileUp
|
2
|
+
module ImageProcessors
|
3
|
+
class MiniMagick < TileUp::ImageProcessor
|
4
|
+
|
5
|
+
def initialize logger
|
6
|
+
require 'mini_magick'
|
7
|
+
super(logger)
|
8
|
+
end
|
9
|
+
|
10
|
+
def width image
|
11
|
+
image.width
|
12
|
+
end
|
13
|
+
|
14
|
+
def height image
|
15
|
+
image.height
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def open_image(image_filename)
|
21
|
+
::MiniMagick::Image.open(image_filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
def scale_image image, scale
|
25
|
+
open_image(image.path).resize("#{100.0 * scale}%")
|
26
|
+
rescue RuntimeError => e
|
27
|
+
logger.error "Failed to scale image, are you sure the original image is large enough (#{image.width} x #{image.height})?"
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
def crop_and_save_image image, crop, filename:, extend_crop: false, extend_color: 'none'
|
32
|
+
logger.verbose "Saving tile: #{crop[:column]}, #{crop[:row]}..."
|
33
|
+
|
34
|
+
::MiniMagick::Tool::Convert.new do |convert|
|
35
|
+
convert << mpc(image).path
|
36
|
+
convert.merge! ['-crop', "#{crop[:width]}x#{crop[:height]}+#{crop[:x]}+#{crop[:y]}"]
|
37
|
+
|
38
|
+
if extend_crop
|
39
|
+
convert.merge! ['-background', extend_color]
|
40
|
+
convert.merge! ['-extent', "#{crop[:width]}x#{crop[:height]}"]
|
41
|
+
end
|
42
|
+
|
43
|
+
convert << filename
|
44
|
+
end
|
45
|
+
|
46
|
+
logger.verbose "Saving tile: #{crop[:column]}, #{crop[:row]}... saved"
|
47
|
+
|
48
|
+
true
|
49
|
+
rescue RuntimeError => e
|
50
|
+
raise e
|
51
|
+
logger.error "Failed to crop image: #{e.message}"
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
def mpc image
|
56
|
+
@mpcs ||= {}
|
57
|
+
@mpcs[image] ||= open_image(image.path).format 'mpc'
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module TileUp
|
2
|
+
module ImageProcessors
|
3
|
+
class RMagick < TileUp::ImageProcessor
|
4
|
+
|
5
|
+
def initialize logger
|
6
|
+
require 'rmagick'
|
7
|
+
super(logger)
|
8
|
+
end
|
9
|
+
|
10
|
+
def width image
|
11
|
+
image.columns
|
12
|
+
end
|
13
|
+
|
14
|
+
def height image
|
15
|
+
image.rows
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def open_image(image_filename)
|
21
|
+
::Magick::Image.read(image_filename).first
|
22
|
+
end
|
23
|
+
|
24
|
+
def scale_image image, scale
|
25
|
+
image.scale(scale)
|
26
|
+
rescue RuntimeError => e
|
27
|
+
logger.error "Failed to scale image, are you sure the original image is large enough (#{image.columns} x #{image.rows})?"
|
28
|
+
false
|
29
|
+
end
|
30
|
+
|
31
|
+
def crop_and_save_image image, crop, filename:, extend_crop: false, extend_color: 'none'
|
32
|
+
cropped_image = image.crop(crop[:x], crop[:y], crop[:width], crop[:height], true)
|
33
|
+
|
34
|
+
# unless told to do otherwise, extend tiles in the last row and column
|
35
|
+
# if they do not fill an entire tile width and height.
|
36
|
+
needs_extension = cropped_image.columns != crop[:width] || cropped_image.rows != crop[:height]
|
37
|
+
|
38
|
+
if extend_crop && needs_extension
|
39
|
+
# defaults to white background color, but transparent is probably
|
40
|
+
# a better default for our purposes.
|
41
|
+
cropped_image.background_color = extend_color
|
42
|
+
# fill to width height, start from top left corner.
|
43
|
+
cropped_image = cropped_image.extent(crop[:width], crop[:height], 0, 0)
|
44
|
+
end
|
45
|
+
|
46
|
+
logger.verbose "Saving tile: #{crop[:column]}, #{crop[:row]}..."
|
47
|
+
cropped_image.write(filename)
|
48
|
+
logger.verbose "Saving tile: #{crop[:column]}, #{crop[:row]}... saved"
|
49
|
+
|
50
|
+
true
|
51
|
+
rescue RuntimeError => e
|
52
|
+
logger.error "Failed to crop image: #{e.message}"
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module TileUp
|
5
|
+
|
6
|
+
# Base logger class, subclass this, do not use directly.
|
7
|
+
class Logger
|
8
|
+
|
9
|
+
def self.build type, level, options = {}
|
10
|
+
case type
|
11
|
+
when 'none' then TileUp::Loggers::None.new(level, options)
|
12
|
+
when ::Logger then TileUp::Logger.new(level, options.merge(logger: type))
|
13
|
+
else TileUp::Loggers::Console.new(level, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.sym_to_severity(sym)
|
18
|
+
severities = {
|
19
|
+
:debug => ::Logger::DEBUG,
|
20
|
+
:info => ::Logger::INFO,
|
21
|
+
:warn => ::Logger::WARN,
|
22
|
+
:error => ::Logger::ERROR,
|
23
|
+
:fatal => ::Logger::FATAL
|
24
|
+
}
|
25
|
+
severity = severities[sym] || ::Logger::UNKNOWN
|
26
|
+
end
|
27
|
+
|
28
|
+
# create logger set to given level
|
29
|
+
# where level is a symbol (:debug, :info, :warn, :error, :fatal)
|
30
|
+
# options may specifiy verbose, which will log more info messages
|
31
|
+
def initialize(level, options = {})
|
32
|
+
@severity = level
|
33
|
+
@logger = options[:logger] if options[:logger]
|
34
|
+
default_options = { verbose: false }
|
35
|
+
@options = OpenStruct.new(default_options.merge(options))
|
36
|
+
end
|
37
|
+
|
38
|
+
def level
|
39
|
+
@level
|
40
|
+
end
|
41
|
+
|
42
|
+
def level=(severity)
|
43
|
+
logger.level = Logger.sym_to_severity(severity)
|
44
|
+
end
|
45
|
+
|
46
|
+
# log an error message
|
47
|
+
def error(message)
|
48
|
+
# note, we always log error messages
|
49
|
+
add(:error, message)
|
50
|
+
end
|
51
|
+
|
52
|
+
# log a regular message
|
53
|
+
def info(message)
|
54
|
+
add(:info, message)
|
55
|
+
end
|
56
|
+
|
57
|
+
def warn(message)
|
58
|
+
add(:warn, message)
|
59
|
+
end
|
60
|
+
|
61
|
+
# log a verbose message
|
62
|
+
def verbose(message)
|
63
|
+
add(:info, message) if verbose?
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# add message to log
|
69
|
+
def add(severity, message)
|
70
|
+
severity = Logger.sym_to_severity(severity)
|
71
|
+
logger.add(severity, message)
|
72
|
+
end
|
73
|
+
|
74
|
+
# is logger in verbose mode?
|
75
|
+
def verbose?
|
76
|
+
@options.verbose
|
77
|
+
end
|
78
|
+
|
79
|
+
# create or return a logger
|
80
|
+
def logger
|
81
|
+
@logger ||= create_logger
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# subclasses should overwrite this method, creating what ever
|
87
|
+
# logger they want to
|
88
|
+
def create_logger
|
89
|
+
raise "You should create your own `create_logger` method"
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TileUp
|
2
|
+
module Loggers
|
3
|
+
class Console < Logger
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def create_logger
|
8
|
+
logger = ::Logger.new(STDOUT)
|
9
|
+
logger.formatter = Proc.new do |sev, time, prg, msg|
|
10
|
+
"#{time.strftime('%H:%M:%S').to_s} => #{msg}\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
logger
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/tileup/tiler.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'tileup/logger'
|
4
|
+
|
5
|
+
module TileUp
|
6
|
+
|
7
|
+
class Tiler
|
8
|
+
|
9
|
+
DEFAULT_OPTIONS = {
|
10
|
+
processor: 'rmagick', # or 'mini_magick'
|
11
|
+
auto_zoom_level: nil,
|
12
|
+
tile_width: 256,
|
13
|
+
tile_height: 256,
|
14
|
+
filename_prefix: 'map_tile',
|
15
|
+
output_dir: '.',
|
16
|
+
logger: 'console',
|
17
|
+
extend_incomplete_tiles: true,
|
18
|
+
extend_color: 'none',
|
19
|
+
verbose: false
|
20
|
+
}.freeze
|
21
|
+
|
22
|
+
attr_accessor :image_processor, :image, :options, :extension, :logger
|
23
|
+
|
24
|
+
def initialize image_filename, options = {}
|
25
|
+
self.options = options = OpenStruct.new(DEFAULT_OPTIONS.merge(options))
|
26
|
+
self.logger = TileUp::Logger.build(options.logger, :info, verbose: options.verbose)
|
27
|
+
self.image_processor = TileUp::ImageProcessor.build(options.processor, logger)
|
28
|
+
self.image = image_processor.open(image_filename)
|
29
|
+
self.extension = image_filename.split(".").last
|
30
|
+
|
31
|
+
%w(tile_width tile_height).each{|dimension| options[dimension] = options[dimension].to_i }
|
32
|
+
|
33
|
+
if options.auto_zoom_level
|
34
|
+
if options.auto_zoom_level == 'auto'
|
35
|
+
options.auto_zoom_level = recommended_auto_zoom_level
|
36
|
+
else
|
37
|
+
options.auto_zoom_level = options.auto_zoom_level.to_i
|
38
|
+
end
|
39
|
+
|
40
|
+
if options.auto_zoom_level > 20
|
41
|
+
logger.warn 'Warning: auto zoom levels hard limited to 20.'
|
42
|
+
options.auto_zoom_level = 20
|
43
|
+
elsif options.auto_zoom_level <= 0
|
44
|
+
options.auto_zoom_level = nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def make_tiles! base_images = build_base_images
|
50
|
+
if base_images
|
51
|
+
result = base_images.map do |base_image|
|
52
|
+
FileUtils.mkdir_p(base_image[:image_path])
|
53
|
+
{ base_image[:image_path] => make_tiles_for_base_image!(base_image[:image], File.join(base_image[:image_path], options.filename_prefix)) }
|
54
|
+
end
|
55
|
+
logger.verbose result
|
56
|
+
logger.info 'Done'
|
57
|
+
result
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_base_images
|
64
|
+
logger.info 'Building base images'
|
65
|
+
|
66
|
+
base_images = base_image_processing_tasks.map do |task|
|
67
|
+
{
|
68
|
+
image: image_processor.scale(image, task[:scale]),
|
69
|
+
image_path: task[:output_dir]
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
if base_images
|
74
|
+
logger.info "Building base images finished."
|
75
|
+
base_images
|
76
|
+
else
|
77
|
+
logger.info 'Building base images failed.'
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def recommended_auto_zoom_level
|
83
|
+
zoom_level = (1..20).detect do |zoom_level|
|
84
|
+
scale = (1.to_f / 2 ** (zoom_level - 1))
|
85
|
+
|
86
|
+
image_processor.width(image) * scale < options.tile_width ||
|
87
|
+
image_processor.height(image) * scale < options.tile_height
|
88
|
+
end
|
89
|
+
|
90
|
+
[1, zoom_level.to_i - 1].max
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def make_tiles_for_base_image!(base_image, filename_prefix)
|
96
|
+
# find image width and height
|
97
|
+
# then find out how many tiles we'll get out of
|
98
|
+
# the image, then use that for the xy offset in crop.
|
99
|
+
num_columns = (image_processor.width(base_image) / options.tile_width.to_f).ceil
|
100
|
+
num_rows = (image_processor.height(base_image) / options.tile_height.to_f).ceil
|
101
|
+
|
102
|
+
logger.info "Tiling image into columns: #{num_columns}, rows: #{num_rows}"
|
103
|
+
|
104
|
+
crops_for(num_columns, num_rows).map do |crop|
|
105
|
+
filename = "#{filename_prefix}_#{crop[:column]}_#{crop[:row]}.#{extension}"
|
106
|
+
is_edge = (crop[:row] == num_rows - 1 || crop[:column] == num_columns - 1)
|
107
|
+
image_processor.crop_and_save(base_image, crop, filename: filename, extend_crop: options.extend_incomplete_tiles && is_edge, extend_color: options.extend_color)
|
108
|
+
filename
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def base_image_processing_tasks
|
113
|
+
if options.auto_zoom_level.nil?
|
114
|
+
[{ output_dir: options.output_dir, scale: 1.0 }]
|
115
|
+
else
|
116
|
+
current_scale = 1.0
|
117
|
+
(1..options.auto_zoom_level).to_a.reverse.map do |zoom_level|
|
118
|
+
task = {
|
119
|
+
output_dir: File.join(options.output_dir, zoom_level.to_s),
|
120
|
+
scale: current_scale
|
121
|
+
}
|
122
|
+
|
123
|
+
current_scale = current_scale / 2
|
124
|
+
task
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def crops_for num_columns, num_rows
|
130
|
+
crops = []
|
131
|
+
|
132
|
+
x = y = column = row = 0
|
133
|
+
while row < num_rows
|
134
|
+
crops << {
|
135
|
+
row: row,
|
136
|
+
column: column,
|
137
|
+
x: column * options.tile_width,
|
138
|
+
y: row * options.tile_height,
|
139
|
+
width: options.tile_width,
|
140
|
+
height: options.tile_height
|
141
|
+
}
|
142
|
+
|
143
|
+
column += 1
|
144
|
+
if column >= num_columns
|
145
|
+
column = 0
|
146
|
+
row += 1
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
crops
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
data/tileup.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
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 = 'moiristo-tileup'
|
8
|
+
spec.version = TileUp::VERSION
|
9
|
+
spec.authors = ['Reinier de Lange']
|
10
|
+
spec.email = 'rein@bookingexperts.nl'
|
11
|
+
|
12
|
+
spec.summary = 'Turn an image into an X,Y tile set for use with JS mapping libraries'
|
13
|
+
spec.description = spec.summary
|
14
|
+
spec.homepage = 'http://github.com/rktjmp/tileup'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^test/})
|
19
|
+
end
|
20
|
+
|
21
|
+
spec.bindir = 'bin'
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'minitest', '~> 5.10'
|
28
|
+
spec.add_development_dependency 'pry'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'rmagick', '~> 2.16'
|
31
|
+
spec.add_development_dependency 'mini_magick', '~> 4.7'
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moiristo-tileup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Reinier de Lange
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.10'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.10'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rmagick
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.16'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.16'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mini_magick
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '4.7'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '4.7'
|
97
|
+
description: Turn an image into an X,Y tile set for use with JS mapping libraries
|
98
|
+
email: rein@bookingexperts.nl
|
99
|
+
executables:
|
100
|
+
- tileup
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- CHANGES.md
|
107
|
+
- Gemfile
|
108
|
+
- Gemfile.lock
|
109
|
+
- LICENCE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- bin/tileup
|
113
|
+
- lib/tileup.rb
|
114
|
+
- lib/tileup/image_processor.rb
|
115
|
+
- lib/tileup/image_processors/mini_magick.rb
|
116
|
+
- lib/tileup/image_processors/rmagick.rb
|
117
|
+
- lib/tileup/logger.rb
|
118
|
+
- lib/tileup/loggers/console.rb
|
119
|
+
- lib/tileup/loggers/none.rb
|
120
|
+
- lib/tileup/tiler.rb
|
121
|
+
- lib/tileup/version.rb
|
122
|
+
- tileup.gemspec
|
123
|
+
homepage: http://github.com/rktjmp/tileup
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata: {}
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - ">="
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.4.8
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: Turn an image into an X,Y tile set for use with JS mapping libraries
|
147
|
+
test_files: []
|