tilecache 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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
+