moiristo-tileup 1.0.0

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.
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: []