tilecache 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Changelog ADDED
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2008-05-07
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
data/README.rdoc ADDED
@@ -0,0 +1,85 @@
1
+ Ruby-TileCache
2
+ by Pascal Ehlert, ODAdata
3
+ http://ruby-tilecache.rubyforge.org
4
+
5
+ == DESCRIPTION
6
+
7
+ ruby-tilecache is an implementation of TileCache from MetaCarta, written in pure Ruby.
8
+ It allows you to cache image tiles either rendered by MapScript or taken from another source.
9
+ Additionally, it supports meta-tiling which nearly eliminates label duplication issues
10
+ because of the tiling.
11
+
12
+ == FEATURES/PROBLEMS
13
+
14
+ * See description.
15
+
16
+ == SYNOPSIS
17
+
18
+ require 'tile_cache'
19
+
20
+ class MapController < ApplicationController
21
+
22
+ # This action handles WMS GetMap requests
23
+ def wms
24
+ wms = TileCache::Services::WMS.new(params)
25
+ map = wms.get_map
26
+
27
+ render :text => map.data, :content_type => map.layer.format
28
+ end
29
+ end
30
+
31
+ == REQUIREMENTS
32
+
33
+ * RMagick for meta-tiling support
34
+ * Ruby-MapScript for Mapserver layers
35
+
36
+ == INSTALL
37
+
38
+ * sudo gem install ruby-mapscript
39
+
40
+ == CONFIGURATION
41
+
42
+ If you are in a Rails environment, ruby-tilecache looks for a configuration file in config/tilecache.yml,
43
+ otherwise it searches ~/.tilecache.yml and /etc/tilecache.yml.
44
+
45
+ As the name suggests, the file format should be YAML, here is an example configuration:
46
+
47
+ cache:
48
+ type: DiskCache
49
+ root: '../tmp/mapcache'
50
+
51
+ basic:
52
+ type: MapServer
53
+ mapfile: app/map/nic.map
54
+ layers: NIC2,NIC1
55
+ maxresolution: 0.01991484375
56
+ levels: 5
57
+ extension: jpeg
58
+ metatile: true
59
+ metabuffer: 0, 0
60
+ bbox: -87.6903, 10.7075, -82.5921, 15.0259
61
+
62
+ In a Rails environment relative paths always refer to RAILS_ROOT, in other environments you should use absolute paths.
63
+
64
+ == LICENSE
65
+
66
+ Copyright (c) 2008 Pascal Ehlert, ODAdata
67
+
68
+ Permission is hereby granted, free of charge, to any person obtaining
69
+ a copy of this software and associated documentation files (the
70
+ 'Software'), to deal in the Software without restriction, including
71
+ without limitation the rights to use, copy, modify, merge, publish,
72
+ distribute, sublicense, and/or sell copies of the Software, and to
73
+ permit persons to whom the Software is furnished to do so, subject to
74
+ the following conditions:
75
+
76
+ The above copyright notice and this permission notice shall be
77
+ included in all copies or substantial portions of the Software.
78
+
79
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
80
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
81
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
82
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
83
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
84
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
85
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gemspec|
7
+ gemspec.rubyforge_project = "tilecache"
8
+ gemspec.name = "tilecache"
9
+ gemspec.summary = "An implementation of TileCache from MetaCarta, written in pure Ruby"
10
+ #gemspec.description = "An implementation of TileCache from MetaCarta, written in pure Ruby"
11
+ gemspec.email = "pascal.ehlert@odadata.eu"
12
+ gemspec.homepage = "http://github.com/pehlert/ruby-tilecache"
13
+ gemspec.authors = ["Pascal Ehlert"]
14
+ gemspec.files.exclude ".gitignore"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.3
@@ -0,0 +1,44 @@
1
+ module TileCache
2
+ class Bounds
3
+ attr_reader :minx, :miny, :maxx, :maxy
4
+
5
+ def self.parse_string(str)
6
+ new(*str.split(",").map { |s| Float(s.strip) })
7
+ end
8
+
9
+ def initialize(minx = -180, miny = -90, maxx = 180, maxy = 90)
10
+ @minx = Float(minx)
11
+ @miny = Float(miny)
12
+ @maxx = Float(maxx)
13
+ @maxy = Float(maxy)
14
+
15
+ if (minx > maxx) || (miny > maxy)
16
+ raise TileCache::InvalidBounds, "Invalid Bounds: #{self}"
17
+ end
18
+ end
19
+
20
+ def to_s
21
+ [@minx, @miny, @maxx, @maxy].join(", ")
22
+ end
23
+
24
+ # Returns this Bboxes resolution
25
+ def resolution(width = 256, height = 256)
26
+ return [(maxx - minx) / width,
27
+ (maxy - miny) / height].max
28
+ end
29
+
30
+ # Returns the maximum resolution possible for a given tile width and height
31
+ def max_resolution(width = 256, height = 256)
32
+ b_width = (maxx - minx).to_f
33
+ b_height = (maxy - miny).to_f
34
+
35
+ if b_width >= b_height
36
+ aspect = (b_width / b_height).ceil
37
+ b_width / (width * aspect)
38
+ else
39
+ aspect = (b_height / b_width).ceil
40
+ b_height / (height * aspect)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ module TileCache
2
+ module Caches
3
+ class Disk
4
+ REQUIRED_ATTRIBUTES = %w{ root }
5
+
6
+ def initialize(settings)
7
+ @root = settings[:root]
8
+ end
9
+
10
+ def get(tile)
11
+ file = key_for_tile(tile)
12
+
13
+ if File.exist?(file)
14
+ File.open(file, File::RDONLY) { |f| tile.data = f.read }
15
+ return true
16
+ else
17
+ return false
18
+ end
19
+ end
20
+
21
+ def store(tile, data = nil)
22
+ tile.data = data if data
23
+ raise CacheError, "Called #store! with no data argument and no data associated with the tile." unless tile.data
24
+
25
+ # Create the full path to the cache file
26
+ file = key_for_tile(tile)
27
+ FileUtils.mkdir_p(File.dirname(file))
28
+
29
+ File.open(file, (File::WRONLY | File::CREAT)) do |f|
30
+ f.sync = true
31
+ f.write(tile.data)
32
+ end
33
+ end
34
+
35
+ protected
36
+ def key_for_tile(tile)
37
+ components = [
38
+ @root,
39
+ tile.layer.name,
40
+ sprintf("%02d", tile.z),
41
+ sprintf("%03d", Integer(tile.x / 1000000)),
42
+ sprintf("%03d", Integer((tile.x / 1000)) % 1000),
43
+ sprintf("%03d", Integer(tile.x) % 1000),
44
+ sprintf("%03d", Integer(tile.y / 1000000)),
45
+ sprintf("%03d", Integer((tile.y / 1000)) % 1000),
46
+ sprintf("%03d.%s", Integer(tile.y) % 1000, tile.layer.extension)
47
+ ]
48
+
49
+ File.join(*components)
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ # Preload all cache handler classes
2
+ Dir[File.dirname(__FILE__) + '/caches/*.rb'].each { |c| require c }
3
+
4
+ module TileCache
5
+ module Caches
6
+ class << self
7
+ def get_handler_class(cache_type)
8
+ class_name = cache_type.sub(/Cache$/, '')
9
+
10
+ if Caches.const_defined?(class_name)
11
+ Caches.const_get(class_name)
12
+ else
13
+ raise InvalidConfiguration, "Invalid cache type attribute: #{type}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,122 @@
1
+ require 'tile_cache/tile'
2
+ require 'tile_cache/meta_tile'
3
+
4
+ module TileCache
5
+ class Layer
6
+ DEFAULT_CONFIGURATION = {
7
+ :bbox => [-180, -90, 180, 90],
8
+ :srs => "EPSG:4326",
9
+ :description => "",
10
+ :size => [256, 256],
11
+ :levels => 20,
12
+ :extension => "png",
13
+ :metatile => false,
14
+ :metasize => [5, 5],
15
+ :metabuffer => [10, 10]
16
+ }
17
+
18
+ attr_reader :name, :description, :layers, :bbox, :size, :units, :srs, :extension, :resolutions
19
+
20
+ def initialize(name, config)
21
+ @name = name
22
+ @description = config[:description]
23
+ @layers = config[:layers] || name
24
+ @bbox = config[:bbox].is_a?(String) ? TileCache::Bounds.parse_string(config[:bbox]) : TileCache::Bounds.new(*config[:bbox])
25
+ @size = config[:size].is_a?(String) ? config[:size].split(",").map { |s| Integer(s.strip) } : config[:size].map { |s| s.to_i }
26
+ @units = config[:units]
27
+ @srs = config[:srs]
28
+
29
+ @extension = config[:extension].downcase
30
+ @extension = 'jpeg' if @extension == 'jpg'
31
+
32
+ @resolutions = parse_resolutions(config[:resolutions], config[:minresolution], config[:maxresolution], config[:levels])
33
+ end
34
+
35
+ # Create a new tile for the given bounds
36
+ def get_tile(bbox)
37
+ coords = get_cell(bbox)
38
+ TileCache::Tile.new(self, *coords)
39
+ end
40
+
41
+ # Fetch or render tile
42
+ def render(tile)
43
+ cache = ConfigParser.instance.cache
44
+
45
+ unless cache.get(tile)
46
+ data = render_tile(tile)
47
+ cache.store(tile, data)
48
+ end
49
+
50
+ return tile
51
+ end
52
+
53
+ def render_bbox(bbox)
54
+ case bbox
55
+ when String
56
+ bounds = Bounds.parse_string(bbox)
57
+ when Bounds
58
+ bounds = bbox
59
+ else
60
+ raise ArgumentError, "Invalid argument for bbox: #{bbox.inspect}"
61
+ end
62
+
63
+ tile = get_tile(bounds)
64
+ render(tile)
65
+ end
66
+
67
+
68
+ # This returns the total number of columns and rows of the grid,
69
+ # based on the given z-indexes resolution
70
+ def grid_limits(z)
71
+ maxcols = (@bbox.maxx - @bbox.minx) / (@resolutions[z] * @size[0])
72
+ maxrows = (@bbox.maxy - @bbox.miny) / (@resolutions[z] * @size[1])
73
+
74
+ return [maxcols.to_i, maxrows.to_i]
75
+ end
76
+
77
+ def format
78
+ return "image/" + @extension
79
+ end
80
+
81
+ protected
82
+ # Returns the z-index for the bboxes resolution
83
+ def level_for_bbox(bbox)
84
+ max_diff = bbox.resolution(*@size) / @size.max
85
+
86
+ if match = @resolutions.detect { |res| (res - bbox.resolution(*@size)).abs < max_diff }
87
+ @resolutions.index(match)
88
+ else
89
+ raise TileCache::InvalidResolution, "Can't find resolution index for #{bbox.resolution}. Available resolutions are #{@resolutions.join(', ')}."
90
+ end
91
+ end
92
+
93
+ # Returns x, y and z coordinates for a given bbox
94
+ def get_cell(bbox)
95
+ # Get exact resolution as specified
96
+ z = level_for_bbox(bbox)
97
+ res = @resolutions[z]
98
+
99
+ x = ((bbox.minx - @bbox.minx) / (res * @size[0])).round
100
+ y = ((bbox.miny - @bbox.miny) / (res * @size[1])).round
101
+
102
+ return [x, y, z]
103
+ end
104
+
105
+ private
106
+ # Calculate resolutions unless given via configuration
107
+ def parse_resolutions(res_list, min_res, max_res, levels)
108
+ case res_list
109
+ when String
110
+ res_list.split(",").map { |r| Float(r.strip) }
111
+ when Array
112
+ res_list.map { |r| Float(r) }
113
+ when NilClass
114
+ max_res ||= @bbox.max_resolution(*@size)
115
+ (0..levels).map { |i| max_res.to_f / 2 ** i }
116
+ else
117
+ raise TileCache::Layers::InvalidConfiguration, "Invalid format of resolutions for layer #{@name}"
118
+ end
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,60 @@
1
+ require 'mapscript'
2
+
3
+ module TileCache
4
+ module Layers
5
+ class MapServer < TileCache::MetaLayer
6
+ include Mapscript
7
+
8
+ VALID_ATTRIBUTES = %w{ maxresolution levels extension metatile metabuffer bbox resolutions width height }
9
+ REQUIRED_ATTRIBUTES = %w{ mapfile layers }
10
+
11
+ attr_reader :mapfile
12
+
13
+ def initialize(name, config)
14
+ @mapfile = config[:mapfile]
15
+ super
16
+ end
17
+
18
+ def render_tile(tile)
19
+ set_metabuffer if @metabuffer
20
+ req = build_request(tile)
21
+
22
+ msIO_installStdoutToBuffer
23
+ map.OWSDispatch(req)
24
+ msIO_stripStdoutBufferContentType
25
+ msIO_getStdoutBufferBytes
26
+ end
27
+
28
+ protected
29
+ def map
30
+ @map ||= MapObj.new(File.join(RAILS_ROOT, @mapfile))
31
+ end
32
+
33
+ def set_metabuffer
34
+ # Don't override the mapfile settings!
35
+ begin
36
+ map.getMetaData("labelcache_map_edge_buffer")
37
+ rescue
38
+ buffer = -@metabuffer.max - 5
39
+ map.setMetaData("labelcache_map_edge_buffer", buffer.to_s)
40
+ end
41
+ end
42
+
43
+ def build_request(tile)
44
+ req = OWSRequest.new
45
+
46
+ req.setParameter("bbox", tile.bounds.to_s)
47
+ req.setParameter("width", tile.size[0].to_s)
48
+ req.setParameter("height", tile.size[1].to_s)
49
+ req.setParameter("srs", @srs)
50
+ req.setParameter("format", format)
51
+ req.setParameter("layers", @layers)
52
+ req.setParameter("request", "GetMap")
53
+ req.setParameter("service", "WMS")
54
+ req.setParameter("version", "1.1.1")
55
+
56
+ return req
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,21 @@
1
+ require 'tile_cache/layer'
2
+ require 'tile_cache/meta_layer'
3
+
4
+ # Preload layer classes
5
+ Dir[File.dirname(__FILE__) + '/layers/*.rb'].each { |c| require c }
6
+
7
+ module TileCache
8
+ module Layers
9
+ class << self
10
+ def get_handler_class(layer_type)
11
+ class_name = layer_type.sub(/Layer$/, '')
12
+
13
+ if Layers.const_defined?(class_name)
14
+ Layers.const_get(class_name)
15
+ else
16
+ raise InvalidConfiguration, "Invalid layer type attribute: #{type}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+ require 'rubygems'
2
+ require 'RMagick'
3
+
4
+ module TileCache
5
+ class MetaLayer < TileCache::Layer
6
+ include Magick
7
+
8
+ attr_reader :metabuffer
9
+
10
+ def initialize(name, config)
11
+ super
12
+
13
+ @metatile = [true, 1, "true", "yes", "1"].include?(config[:metatile])
14
+ @metasize = config[:metasize].is_a?(String) ? config[:metasize].split(",").map { |s| Integer(s.strip) } : config[:metasize].map { |s| Integer(s) }
15
+ @metabuffer = config[:metabuffer].is_a?(String) ? config[:metabuffer].split(",").map { |s| Integer(s.strip) } : config[:metabuffer].map { |s| Integer(s) }
16
+ end
17
+
18
+ # Columns and rows for a given z-indexes resolution.
19
+ # Returns 1,1 if meta-tiling is disabled, otherwise the desired metasize or grid limits.
20
+ def meta_limits(z)
21
+ return [1, 1] unless @metatile
22
+
23
+ limits = grid_limits(z)
24
+ maxcols = [@metasize[0], limits[0] + 1].min
25
+ maxrows = [@metasize[1], limits[1] + 1].min
26
+
27
+ [maxcols, maxrows]
28
+ end
29
+
30
+ def get_meta_tile(tile)
31
+ x = (tile.x / @metasize[0]).to_i
32
+ y = (tile.y / @metasize[1]).to_i
33
+
34
+ MetaTile.new(self, x, y, tile.z)
35
+ end
36
+
37
+ def render_meta_tile(metatile, tile)
38
+ data = render_tile(metatile)
39
+ image = Image.from_blob(data).first
40
+
41
+ (meta_cols, meta_rows) = meta_limits(metatile.z)
42
+ meta_height = meta_rows * @size[1] + 2 * @metabuffer[1]
43
+
44
+ 0.upto(meta_cols-1) do |c|
45
+ 0.upto(meta_rows-1) do |r|
46
+ minx = c * @size[0] + @metabuffer[0]
47
+ miny = r * @size[1] + @metabuffer[1]
48
+
49
+ subimage = image.crop(SouthWestGravity, minx, miny, @size[0], @size[1])
50
+ x = metatile.x * @metasize[0] + c
51
+ y = metatile.y * @metasize[1] + r
52
+ subtile = TileCache::Tile.new(self, x, y, metatile.z)
53
+ TileCache.cache.store(subtile, subimage.to_blob)
54
+
55
+ if x == tile.x && y == tile.y
56
+ tile.data = subimage.to_blob
57
+ end
58
+ end
59
+ end
60
+
61
+ return tile.data
62
+ end
63
+
64
+ def render(tile)
65
+ if @metatile
66
+ metatile = get_meta_tile(tile)
67
+
68
+ unless TileCache.cache.get(tile)
69
+ render_meta_tile(metatile, tile)
70
+ end
71
+
72
+ return tile
73
+ else
74
+ super
75
+ end
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,31 @@
1
+ module TileCache
2
+ class MetaTile < TileCache::Tile
3
+ # Returns the tile's size without meta-buffers
4
+ def size_without_buffers
5
+ (meta_cols, meta_rows) = @layer.meta_limits(@z)
6
+ return [@layer.size[0] * meta_cols, @layer.size[1] * meta_rows]
7
+ end
8
+
9
+ # Returns this tile's size including buffers
10
+ def size
11
+ actual = size_without_buffers
12
+ return [actual[0] + @layer.metabuffer[0] * 2,
13
+ actual[1] + @layer.metabuffer[1] * 2]
14
+ end
15
+
16
+ def bounds
17
+ tilesize = size_without_buffers
18
+ res = @layer.resolutions[@z]
19
+ buffer = [res * @layer.metabuffer[0], res * @layer.metabuffer[1]]
20
+ meta_width = res * tilesize[0]
21
+ meta_height = res * tilesize[1]
22
+
23
+ minx = @layer.bbox.minx + @x * meta_width - buffer[0]
24
+ miny = @layer.bbox.miny + @y * meta_height - buffer[1]
25
+ maxx = minx + meta_width + 2 * buffer[0]
26
+ maxy = miny + meta_height + 2 * buffer[1]
27
+
28
+ return TileCache::Bounds.new(minx, miny, maxx, maxy)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module TileCache
2
+ module Services
3
+ class WMS
4
+ FIELDS = %{ bbox srs width height format layers styles }
5
+
6
+ def initialize(params)
7
+ @params = parse_request(params)
8
+ end
9
+
10
+ def get_map
11
+ bbox = Bounds.parse_string(@params[:bbox])
12
+ layer = TileCache.layers[@params[:layers]]
13
+ # TODO: Move into config parser
14
+ raise LayerNotFound, "Can't find layer '#{@params[:layers]}' in configuration" unless layer
15
+ tile = layer.get_tile(bbox)
16
+
17
+ layer.render(tile)
18
+ end
19
+
20
+ private
21
+ def parse_request(params)
22
+ parsed = {}
23
+ params.each do |k, v|
24
+ key = k.downcase
25
+ parsed[key.to_sym] = v if FIELDS.include?(key)
26
+ end
27
+ parsed
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + '/services/*.rb'].each { |c| require c }
@@ -0,0 +1,28 @@
1
+ module TileCache
2
+ class Tile
3
+ attr_reader :layer, :x, :y, :z, :size
4
+ attr_accessor :data
5
+
6
+ def initialize(layer, x, y, z)
7
+ @layer = layer
8
+ @x = x
9
+ @y = y
10
+ @z = z
11
+ @data = nil
12
+ end
13
+
14
+ def bounds
15
+ res = @layer.resolutions[@z]
16
+ minx = @layer.bbox.minx + (res * @x * @layer.size[0])
17
+ miny = @layer.bbox.miny + (res * @y * @layer.size[1])
18
+ maxx = @layer.bbox.minx + (res * (@x + 1) * @layer.size[0])
19
+ maxy = @layer.bbox.miny + (res * (@y + 1) * @layer.size[1])
20
+
21
+ return TileCache::Bounds.new(minx, miny, maxx, maxy)
22
+ end
23
+
24
+ def size
25
+ @layer.size
26
+ end
27
+ end
28
+ end
data/lib/tile_cache.rb ADDED
@@ -0,0 +1,112 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'active_support/core_ext/hash/reverse_merge.rb'
5
+ require 'yaml'
6
+
7
+ require 'tile_cache/bounds'
8
+ require 'tile_cache/services'
9
+ require 'tile_cache/caches'
10
+ require 'tile_cache/layers'
11
+
12
+ module TileCache
13
+ CONFIG_PATHS = [
14
+ Pathname.new("/etc/tilecache.yml"),
15
+ Pathname.new("#{ENV['HOME']}/.tilecache.yml"),
16
+ Pathname.new("#{Dir.pwd}/config/tilecache.yml")
17
+ ]
18
+
19
+ # Exception classes
20
+ class InvalidBounds < StandardError; end
21
+ class InvalidResolution < StandardError; end
22
+ class InvalidConfiguration < StandardError; end
23
+ class CacheError < StandardError; end
24
+ class LayerNotFound < StandardError; end
25
+
26
+ class << self
27
+ attr_accessor :cache, :layers
28
+
29
+ # Initialize TileCache framework, read out configuration and setup cache and layer classes
30
+ def initialize!
31
+ read_configuration
32
+
33
+ initialize_cache
34
+ initialize_layers_pool
35
+ end
36
+
37
+ private
38
+ def read_configuration
39
+ # Select first existing path from hardcoded list
40
+ path = CONFIG_PATHS.reverse.find(&:exist?)
41
+
42
+ @config = YAML.load(File.read(path))
43
+ rescue => e
44
+ raise InvalidConfiguration, "Can't read configuration from #{CONFIG_PATHS.join(', ')}: #{e.message}"
45
+ end
46
+
47
+ def initialize_cache
48
+ config = @config.delete('cache')
49
+ raise InvalidConfiguration, "Missing cache section in your configuration file" unless config
50
+ config.symbolize_keys!
51
+
52
+ klass = Caches.get_handler_class(config.delete(:type))
53
+ validate_attributes!(klass, config)
54
+ merge_with_defaults!(klass, config)
55
+
56
+ TileCache.cache = klass.new(config)
57
+ end
58
+
59
+ def initialize_layers_pool
60
+ layers = @config.delete('layers')
61
+ raise InvalidConfiguration, "Please configure at least one layer" unless layers
62
+
63
+ TileCache.layers = {}
64
+ layers.each do |layer, settings|
65
+ settings.symbolize_keys!
66
+
67
+ klass = Layers.get_handler_class(settings.delete(:type))
68
+ validate_attributes!(klass, settings)
69
+ merge_with_defaults!(klass, settings)
70
+
71
+ TileCache.layers.store(layer, klass.new(layer, settings))
72
+ end
73
+ end
74
+
75
+ # Reads DEFAULT_CONFIGURATION hash from the class specified in settings[:class]
76
+ # and merges settings hash with these values. If no DEFAULT_CONFIGURATION has
77
+ # been specified, it doesn't change settings.
78
+ def merge_with_defaults!(klass, config)
79
+ defaults = klass.const_get("DEFAULT_CONFIGURATION").symbolize_keys rescue {}
80
+ config.reverse_merge!(defaults)
81
+ end
82
+
83
+ # Check for the validity of configuration attributes
84
+ # If any attributes that are not specified in the classes VALID_ATTRIBUTES
85
+ # constant appear in the configuration file or if there are attributes from REQUIRED_ATTRIBUTES missing
86
+ # this raises TileCache::InvalidConfiguration
87
+ def validate_attributes!(klass, config)
88
+ valid_attributes = klass.const_get("VALID_ATTRIBUTES") rescue []
89
+ required_attributes = klass.const_get("REQUIRED_ATTRIBUTES") rescue []
90
+ # Merge with required attributes so that we don't have to specify them twice
91
+ valid_attributes |= required_attributes
92
+
93
+ unless valid_attributes.empty?
94
+ config.except(:name).each_key do |a|
95
+ unless valid_attributes.include?(a.to_s)
96
+ raise InvalidConfiguration, "Unrecognized attribute in #{config[:name]}: #{a}"
97
+ end
98
+ end
99
+ end
100
+
101
+ unless required_attributes.empty?
102
+ required_attributes.each do |a|
103
+ unless config.include?(a.to_sym)
104
+ raise InvalidConfiguration, "Missing attribute in #{config[:name]}: #{a}"
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ TileCache.initialize!
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tilecache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Pascal Ehlert
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-26 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: pascal.ehlert@odadata.eu
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - Changelog
26
+ - README.rdoc
27
+ - Rakefile
28
+ - VERSION
29
+ - lib/tile_cache.rb
30
+ - lib/tile_cache/bounds.rb
31
+ - lib/tile_cache/caches.rb
32
+ - lib/tile_cache/caches/disk.rb
33
+ - lib/tile_cache/layer.rb
34
+ - lib/tile_cache/layers.rb
35
+ - lib/tile_cache/layers/map_server.rb
36
+ - lib/tile_cache/meta_layer.rb
37
+ - lib/tile_cache/meta_tile.rb
38
+ - lib/tile_cache/services.rb
39
+ - lib/tile_cache/services/wms.rb
40
+ - lib/tile_cache/tile.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/pehlert/ruby-tilecache
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options:
47
+ - --charset=UTF-8
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project: tilecache
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: An implementation of TileCache from MetaCarta, written in pure Ruby
69
+ test_files: []
70
+