moiristo-tileup 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ test/out
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+
3
+ sudo: false
4
+ cache: bundler
5
+
6
+ rvm:
7
+ - 2.1.10
8
+ - 2.2.3
9
+ - 2.3.4
10
+
11
+ gemfile:
12
+ - Gemfile
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in foo.gemspec
4
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << 'test'
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.verbose = true
7
+ end
8
+
9
+ desc 'Run tests'
10
+ task default: :test
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
@@ -0,0 +1,18 @@
1
+ module TileUp
2
+ module Loggers
3
+ class None < Logger
4
+
5
+ class NullLogger < ::Logger
6
+ def initialize(*args); end
7
+ def add(*args, &block); end
8
+ end
9
+
10
+ private
11
+
12
+ def create_logger
13
+ NullLogger.new
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module TileUp
2
+ VERSION = '1.0.0'
3
+ 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: []