class DZT::Tiler

Constants

DEFAULT_OVERWRITE_FLAG
DEFAULT_QUALITY
DEFAULT_TILE_FORMAT
DEFAULT_TILE_OVERLAP
DEFAULT_TILE_SIZE

Defaults

Public Class Methods

new(options) click to toggle source

Generates the DZI-formatted tiles and sets necessary metadata on this object.

@param source Magick::Image, or filename of image to be used for tiling @param quality Image compression quality (default: 75) @param format Format for output tiles (default: “jpg”) @param size Size, in pixels, for tile squares (default: 512) @param overlap Size, in pixels, of the overlap between tiles (default: 2) @param overwrite Whether or not to overwrite if the destination exists (default: false) @param storage Either an instance of S3Storage or FileStorage

# File lib/dzt/tiler.rb, line 23
def initialize(options)
  @tile_source = options[:source]
  fail 'Missing options[:source].' unless @tile_source

  @tile_source = Magick::Image.read(@tile_source)[0] if @tile_source.is_a?(String)
  @tile_size = options[:size] || DEFAULT_TILE_SIZE
  @tile_overlap = options[:overlap] || DEFAULT_TILE_OVERLAP
  @tile_format  = options[:format] || DEFAULT_TILE_FORMAT

  @max_tiled_height = @tile_source.rows
  @max_tiled_width = @tile_source.columns

  @tile_quality = options[:quality] || DEFAULT_QUALITY
  @overwrite = options[:overwrite] || DEFAULT_OVERWRITE_FLAG
  @storage = options[:storage]
end

Public Instance Methods

slice!() { |current_level_storage_dir| ... } click to toggle source

Generates the DZI-formatted tiles and sets necessary metadata on this object. Uses a default tile size of 512 pixels, with a default overlap of 2 pixel.

# File lib/dzt/tiler.rb, line 44
def slice!(&_block)
  fail "Output #{@destination} already exists!" if ! @overwrite && @storage.exists?

  image = @tile_source.dup
  orig_width = image.columns
  orig_height = image.rows

  # iterate over all levels (= zoom stages)
  max_level(orig_width, orig_height).downto(0) do |level|
    width = image.columns
    height = image.rows

    current_level_storage_dir = @storage.storage_location(level)
    @storage.mkdir(current_level_storage_dir)
    yield current_level_storage_dir if block_given?

    # iterate over columns
    x = 0
    col_count = 0
    while x < width
      # iterate over rows
      y = 0
      row_count = 0
      while y < height
        dest_path = File.join(current_level_storage_dir, "#{col_count}_#{row_count}.#{@tile_format}")
        tile_width, tile_height = tile_dimensions(x, y, @tile_size, @tile_overlap)

        save_cropped_image(image, dest_path, x, y, tile_width, tile_height, @tile_quality)

        y += (tile_height - (2 * @tile_overlap))
        row_count += 1
      end
      x += (tile_width - (2 * @tile_overlap))
      col_count += 1
    end

    image.resize!(0.5)
  end

  image.destroy!
end

Protected Instance Methods

max_level(width, height) click to toggle source

Calculates how often an image with given dimension can be divided by two until 1x1 px are reached.

# File lib/dzt/tiler.rb, line 104
def max_level(width, height)
  (Math.log([width, height].max) / Math.log(2)).ceil
end
save_cropped_image(src, dest, x, y, width, height, quality = 75) click to toggle source

Crops part of src image and writes it to dest path.

Params: src: may be an Magick::Image object or a path to an image.

dest: path where cropped image should be stored.
x, y: offset from upper left corner of source image.
width, height: width and height of cropped image.
quality: compression level 0-100 (or 0.0-1.0), lower number means higher compression.
# File lib/dzt/tiler.rb, line 115
def save_cropped_image(src, dest, x, y, width, height, quality = 75)
  if src.is_a? Magick::Image
    img = src
  else
    img = Magick::Image.read(src).first
  end

  quality *= 100 if quality < 1

  # The crop method retains the offset information in the cropped image.
  # To reset the offset data, adding true as the last argument to crop.
  cropped = img.crop(x, y, width, height, true)
  @storage.write(cropped, dest, quality: quality)
end
tile_dimensions(x, y, tile_size, overlap) click to toggle source

Determines width and height for tiles, dependent of tile position. Center tiles have overlapping on each side. Borders have no overlapping on the border side and overlapping on all other sides. Corners have only overlapping on the right and lower border.

# File lib/dzt/tiler.rb, line 92
def tile_dimensions(x, y, tile_size, overlap)
  overlapping_tile_size = tile_size + (2 * overlap)
  border_tile_size      = tile_size + overlap

  tile_width  = (x > 0) ? overlapping_tile_size : border_tile_size
  tile_height = (y > 0) ? overlapping_tile_size : border_tile_size

  [tile_width, tile_height]
end