tairu 0.9.0 → 0.10.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.
data/README.md CHANGED
@@ -2,12 +2,11 @@ tairu ... simple map tile server
2
2
 
3
3
  In development mode (irb): `rake console`
4
4
 
5
- To run: `tairu -c /path/to/config/file`
5
+ To run from bin: `tairu --config /path/to/config/file`
6
6
 
7
7
  Example config file:
8
8
 
9
9
  ```ruby
10
-
11
10
  name: tairu_config_example
12
11
  cache:
13
12
  type: memory
@@ -17,17 +16,12 @@ layers:
17
16
  tileset: geography-class.mbtiles
18
17
  location: ~/.tairu/tilesets
19
18
  format: png
20
-
21
19
  ```
22
20
 
23
- Tairu relies on a valid configuration object assigned to `Tairu::CONFIG`
24
-
25
- This may be read from a yaml file (see example above) using `Tairu::Configuration.config_from_file(file_name)`
26
-
27
- or by assigning default values for layers, cache, and name (optional):
21
+ Configuration may be read from a yaml file (see example above) using `Tairu.config_from_file(file_name)`
22
+ or by passing values for layers, cache, and name (optional) into a configuration block:
28
23
 
29
24
  ```ruby
30
-
31
25
  layers = {
32
26
  'geo' => {
33
27
  'provider' => 'mbtiles',
@@ -37,10 +31,20 @@ layers = {
37
31
  }
38
32
  }
39
33
 
40
- cache = Tairu::Configuration.start_cache('memory', {})
41
-
42
- name = 'tairu_config_example'
34
+ cache = {
35
+ 'type' => 'redis',
36
+ 'options' => {
37
+ 'host' => 'localhost',
38
+ 'port' => '6379',
39
+ 'db' => 0
40
+ }
41
+ }
43
42
 
44
- Tairu::CONFIG = Tairu::Configuration.new(layers, cache, name)
43
+ Tairu.configure do |config|
44
+ config.name = 'tairu_config_example'
45
+ config.layers = layers
46
+ config.cache = cache
47
+ end
48
+ ```
45
49
 
46
- ```
50
+ NOTE: If no cache is passed in, it will default to the memory cache
data/bin/tairu CHANGED
@@ -11,7 +11,10 @@ OptionParser.new do |o|
11
11
  o.banner = 'Usage: tairu.rb [options]'
12
12
 
13
13
  o.on('-v', '--version', '') do
14
- require 'tairu'
14
+ unless defined?(Tairu)
15
+ require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'tairu', 'version')
16
+ end
17
+
15
18
  puts "Tairu v#{Tairu::VERSION}"
16
19
  exit(0)
17
20
  end
@@ -30,10 +33,11 @@ OptionParser.new do |o|
30
33
  end.parse!
31
34
 
32
35
  if opts[:config]
33
- require 'tairu'
36
+ unless defined?(Tairu)
37
+ require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'tairu')
38
+ end
34
39
 
35
- Tairu::CONFIG = Tairu::Configuration.config_from_file(opts[:config])
36
- Tairu::CACHE = Tairu::CONFIG.cache
40
+ Tairu.config_from_file(opts[:config])
37
41
  else
38
42
  puts "No valid config file specified. Use -c / --config option."
39
43
  exit(1)
@@ -45,7 +49,7 @@ server = ::Puma::Server.new(app)
45
49
  host = opts[:host] || '0.0.0.0'
46
50
  port = opts[:port] || 8080
47
51
  server.add_tcp_listener host, port
48
- min_threads, max_threads = 0, 16
52
+ min_threads, max_threads = 4, 16
49
53
  server.min_threads = min_threads
50
54
  server.max_threads = max_threads
51
55
 
data/config.ru CHANGED
File without changes
data/lib/tairu.rb CHANGED
@@ -1,36 +1,40 @@
1
1
  $:.push File.expand_path(File.join(File.dirname(__FILE__), 'tairu'))
2
2
 
3
+ require 'rubygems'
3
4
  require 'sequel'
4
5
  require 'cache'
5
6
  require 'store'
6
7
  require 'coordinate'
7
8
  require 'tile'
8
9
  require 'configuration'
10
+ require 'version'
9
11
 
10
12
  module Tairu
11
- TILE_404 = Tairu::Tile.new(File.read(File.join(File.expand_path(File.dirname(__FILE__)), 'tairu', 'images', '404.png')), 'image/png')
13
+ extend self
14
+ extend Configuration
12
15
 
13
- def self.get_tile(name, coord, format = nil)
14
- tileset = Tairu::CONFIG.layers[name]
16
+ TILE_404 = Tairu::Tile.new('', 'image/png')
15
17
 
16
- unless tileset.nil?
17
- tile = Tairu::CACHE.get(tileset, coord)
18
+ class << self
19
+ attr_accessor :logger
18
20
 
19
- if tile.nil?
20
- provider = Tairu::Store::TYPES[tileset['provider']]
21
- provider_tile = provider.get(name, tileset['tileset'], coord, tileset['format'])
21
+ def get_tile(name, coord, format=nil)
22
+ tileset = Tairu.tilesets[name]
23
+
24
+ return TILE_404 if tileset.nil?
25
+
26
+ tile = Tairu.cache.get(name, coord)
22
27
 
23
- unless provider_tile.nil?
24
- tile = provider_tile
25
- Tairu::CACHE.add(tileset, coord, tile)
28
+ if tile.nil?
29
+ tile = tileset.get(coord, Tairu.layers[name]['format'])
30
+ unless tile.nil?
31
+ Tairu.cache.add(name, coord, tile)
26
32
  else
27
33
  tile = TILE_404
28
34
  end
29
35
  end
30
- else
31
- tile = TILE_404
32
- end
33
36
 
34
- tile
37
+ tile
38
+ end
35
39
  end
36
- end
40
+ end
data/lib/tairu/cache.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  require 'cache/memory'
2
2
  require 'cache/disk'
3
+ require 'cache/redis_cache'
3
4
 
4
5
  module Tairu
5
6
  module Cache
6
7
  TYPES = {
7
8
  'memory' => Tairu::Cache::Memory,
8
- 'disk' => Tairu::Cache::Disk
9
+ 'disk' => Tairu::Cache::Disk,
10
+ 'redis' => Tairu::Cache::RedisCache
9
11
  }
10
12
 
11
13
  Key = Struct.new(:layer, :coord, :format)
12
14
  end
13
- end
15
+ end
@@ -3,11 +3,10 @@ require 'fileutils'
3
3
  module Tairu
4
4
  module Cache
5
5
  class Disk
6
- def initialize(options = {})
6
+ def initialize(options={})
7
7
  raise "No path specified." unless options['path']
8
8
  @path = options['path']
9
9
  @expire = options['expire'] || 300
10
- @tiles = {}
11
10
  end
12
11
 
13
12
  def lock_write(path, data)
@@ -40,19 +39,18 @@ module Tairu
40
39
  data
41
40
  end
42
41
 
43
- def add(layer, coord, tile)
42
+ def add(name, coord, tile)
44
43
  expire = Time.now + @expire
45
- tileset = layer['tileset']
46
- base_path = File.join(File.expand_path(@path), tileset, "#{coord.zoom}", "#{coord.column}")
44
+ base_path = File.join(File.expand_path(@path), name, "#{coord.zoom}", "#{coord.column}")
47
45
  FileUtils.mkdir_p(base_path)
48
- path = File.join(base_path, "#{coord.row}.#{layer['format']}")
46
+ path = File.join(base_path, "#{coord.row}.#{Tairu.config.layers[name]['format']}")
49
47
  lock_write(path, tile.data)
50
- purge_expired(layer)
48
+ purge_expired(name)
51
49
  end
52
50
 
53
- def get(layer, coord)
54
- tileset = layer['tileset']
55
- path = File.join(File.expand_path(@path), tileset, "#{coord.zoom}", "#{coord.column}", "#{coord.row}.#{layer['format']}")
51
+ def get(name, coord)
52
+ layer = Tairu.config.layers[name]
53
+ path = File.join(File.expand_path(@path), name, "#{coord.zoom}", "#{coord.column}", "#{coord.row}.#{layer['format']}")
56
54
  data = lock_read(path)
57
55
 
58
56
  return nil if data.nil?
@@ -60,10 +58,11 @@ module Tairu
60
58
  Tairu::Tile.new(data, layer['format'])
61
59
  end
62
60
 
63
- def purge_expired(layer)
64
- Dir.glob(File.join(File.expand_path(@path), "**/*.#{layer['format']}")).each do |f|
61
+ def purge_expired(name)
62
+ Dir.glob(File.join(File.expand_path(@path), "**/*.#{Tairu.config.layers[name]['format']}")).each do |f|
65
63
  FileUtils.rm(f) if File.mtime(f) < (Time.now - @expire)
66
64
  end
65
+ Dir[File.join(File.expand_path(@path), '**/*')].select {|d| File.directory?(d)}.select {|d| (Dir.entries(d) - %w[. ..]).empty?}.each {|d| Dir.rmdir(d)}
67
66
  end
68
67
  end
69
68
  end
@@ -1,19 +1,19 @@
1
1
  module Tairu
2
2
  module Cache
3
3
  class Memory
4
- def initialize(options = nil)
4
+ def initialize(options=nil)
5
5
  @tiles = {}
6
6
  end
7
7
 
8
- def add(layer, coord, tile, age = 300) # format?
9
- key = Tairu::Cache::Key.new(layer, coord)
8
+ def add(name, coord, tile, age=300)
9
+ key = Tairu::Cache::Key.new(name, coord)
10
10
  expire = Time.now + age
11
11
  @tiles[key] = {tile: tile, expire: expire}
12
- :purge_expired
12
+ purge_expired
13
13
  end
14
14
 
15
- def get(layer, coord) # format?
16
- key = Tairu::Cache::Key.new(layer, coord)
15
+ def get(name, coord)
16
+ key = Tairu::Cache::Key.new(name, coord)
17
17
  tile = @tiles[key]
18
18
 
19
19
  return nil if tile.nil?
@@ -31,4 +31,4 @@ module Tairu
31
31
  end
32
32
  end
33
33
  end
34
- end
34
+ end
@@ -0,0 +1,45 @@
1
+ require 'connection_pool'
2
+ require 'redis'
3
+ require 'base64'
4
+ gem 'json', '1.7.7'
5
+ require 'json'
6
+ require 'multi_json'
7
+
8
+ module Tairu
9
+ module Cache
10
+ class RedisCache
11
+ def initialize(options={'host' => 'localhost', 'port' => 6379, 'db' => 0})
12
+ if options['url']
13
+ redis_url = options['url']
14
+ else
15
+ db = options['db'] || 0
16
+ redis_url = "redis://#{options['host']}:#{options['port']}/#{db}"
17
+ end
18
+
19
+ @pool = ConnectionPool::Wrapper.new(timeout: 1, size: 4) do
20
+ Redis.connect(url: redis_url, driver: 'ruby')
21
+ end
22
+ end
23
+
24
+ def add(name, coord, tile, age=300)
25
+ key = "#{name}_#{coord.zoom}_#{coord.column}_#{coord.row}"
26
+ expire = Time.now.to_i + age
27
+ tile = Base64.encode64(tile.data)
28
+ @pool.set(key, MultiJson.dump({tile: tile, format: Tairu.config.layers[name]['format']}))
29
+ @pool.expire(key, expire)
30
+ end
31
+
32
+ def get(name, coord)
33
+ key = "#{name}_#{coord.zoom}_#{coord.column}_#{coord.row}"
34
+ value = @pool.get(key)
35
+
36
+ if value.nil?
37
+ return nil
38
+ else
39
+ tile_hash = MultiJson.load(value)
40
+ Tairu::Tile.new(Base64.decode64(tile_hash['tile']), tile_hash['format'])
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,54 +1,69 @@
1
1
  require 'yaml'
2
2
 
3
3
  module Tairu
4
- class Configuration
5
- attr_accessor :layers, :cache, :name
4
+ module Configuration
5
+ attr_accessor :layers, :cache, :name, :tilesets
6
6
 
7
- def initialize(layers, cache, name = nil)
8
- @layers = layers
9
- @cache = cache
10
- @name = name
7
+ def configure
8
+ yield self
9
+ configure_layers
10
+ configure_cache
11
+ configure_tilesets
11
12
  end
12
13
 
13
- def self.config_from_file(file)
14
- file = File.expand_path(file)
15
-
16
- if File.exists?(file)
17
- data = YAML.load_file(file)
14
+ def configure_cache
15
+ if self.cache
16
+ unless self.cache.instance_of? Tairu::Cache
17
+ if self.cache.instance_of? Hash
18
+ if self.cache['type']
19
+ options = self.cache['options'] || nil
20
+ self.cache = start_cache(self.cache['type'], options)
21
+ else
22
+ raise RuntimeError.new('No cache type sepecified')
23
+ end
24
+ end
25
+ end
18
26
  else
19
- raise 'Configuration file not found at specified location.'
27
+ self.cache = start_cache
20
28
  end
29
+ end
30
+
31
+ def configure_layers
32
+ raise RuntimeError.new('At least one layer must be specified') unless self.layers
33
+ end
21
34
 
22
- name = data['name'] if data['name']
35
+ def configure_tilesets
36
+ self.tilesets = {}
23
37
 
24
- if data['layers']
25
- layers = data['layers']
26
- else
27
- raise 'Layers must be specified.'
38
+ self.layers.each do |k,v|
39
+ self.tilesets[k] = Tairu::Store::TYPES[v['provider'].downcase].new(k)
28
40
  end
41
+ end
29
42
 
30
- if data['cache'] && data['cache']['type']
31
- cache_type = data['cache']['type']
32
- options = data['cache']['options'] ? data['cache']['options'] : {}
43
+ def config_from_file(file)
44
+ file = File.expand_path(file)
33
45
 
34
- cache = start_cache(cache_type, options)
46
+ if File.exists?(file)
47
+ data = YAML.load_file(file)
48
+ else
49
+ raise 'Configuration file not found at specified location.'
35
50
  end
36
51
 
37
- cache = Tairu::Cache::Memory.new if cache.nil?
38
-
39
- config = Configuration.new(layers, cache, name)
52
+ raise RuntimeError.new('At least one layer must be specified') unless data['layers']
40
53
 
41
- config
54
+ configure do |config|
55
+ config.name = data['name']
56
+ config.layers = data['layers']
57
+ config.cache = data['cache']
58
+ end
42
59
  end
43
60
 
44
- def self.start_cache(cache_type = nil, options = nil)
61
+ def start_cache(cache_type=nil, options=nil)
45
62
  if cache_type
46
- cache = Tairu::Cache::TYPES[cache_type].new(options)
63
+ Tairu::Cache::TYPES[cache_type].new(options)
47
64
  else
48
- cache = Tairu::Cache::Memory.new
65
+ Tairu::Cache::Memory.new
49
66
  end
50
-
51
- cache
52
67
  end
53
68
  end
54
- end
69
+ end
File without changes
data/lib/tairu/server.rb CHANGED
@@ -3,10 +3,23 @@ require File.join(File.dirname(__FILE__), '..', 'tairu')
3
3
 
4
4
  module Tairu
5
5
  class Server < Sinatra::Base
6
- get '/:tileset/:zoom/:row/:col.grid.json' do
6
+ get '/:tileset/info' do
7
+ tileset = Tairu.tilesets[params[:tileset]]
8
+
9
+ status 404 if tileset.nil?
10
+
11
+ content_type :json
12
+ tileset.info.to_json
13
+ end
14
+
15
+ get '/:tileset/:zoom/:col/:row.grid.json' do
16
+ status 404 unless Tairu.layers[params[:tileset]]['provider'] == 'mbtiles'
17
+
18
+ tileset = Tairu.tilesets[params[:tileset]]
19
+ status 404 if tileset.nil?
20
+
7
21
  coord = Tairu::Coordinate.new(Integer(params[:row]), Integer(params[:col]), Integer(params[:zoom]))
8
- tileset = Tairu::CONFIG.layers[params[:tileset]]
9
- tile = Tairu::Store::MBTiles.get_grid(params[:tileset], tileset['tileset'], coord)
22
+ tile = tileset.get_grid(coord)
10
23
 
11
24
  callback = params.delete('callback')
12
25
 
@@ -19,7 +32,7 @@ module Tairu
19
32
  end
20
33
  end
21
34
 
22
- get '/:tileset/:zoom/:row/:col' do
35
+ get '/:tileset/:zoom/:col/:row' do
23
36
  coord = Tairu::Coordinate.new(Integer(params[:row]), Integer(params[:col]), Integer(params[:zoom]))
24
37
  tile = Tairu.get_tile(params[:tileset], coord)
25
38
 
@@ -28,4 +41,4 @@ module Tairu
28
41
  tile.data
29
42
  end
30
43
  end
31
- end
44
+ end
data/lib/tairu/store.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'store/mbtiles'
2
2
  require 'store/esri'
3
+ require 'store/tms'
3
4
 
4
5
  module Tairu
5
6
  module Store
6
7
  TYPES = {
7
8
  'mbtiles' => Tairu::Store::MBTiles,
8
- 'esri' => Tairu::Store::Esri
9
+ 'esri' => Tairu::Store::Esri,
10
+ 'tms' => Tairu::Store::TMS
9
11
  }
10
12
  end
11
13
  end
@@ -1,35 +1,37 @@
1
1
  module Tairu
2
2
  module Store
3
3
  class Esri
4
- def self.decode_hex(hex_value)
5
- Integer("0x#{hex_value}")
6
- end
7
-
8
- def self.encode_hex(int_value, length = 8)
9
- length = "%02i" % length
10
- "%#{length}x" % int_value
4
+ def initialize(layer)
5
+ @tileset = File.join(File.expand_path(Tairu.layers[layer]['location']), Tairu.layers[layer]['tileset'])
11
6
  end
12
7
 
13
- def self.get(layer, tileset, coord, format = 'png')
14
- loc = File.expand_path(Tairu::CONFIG.layers[layer]['location'])
15
- path = File.join(loc, tileset, 'Layers', '_alllayers', "L#{'%02i' % coord.zoom}", "R#{encode_hex(coord.row)}", "C#{encode_hex(coord.column)}.#{format}")
8
+ def get(coord, format='png')
9
+ path = File.join(@tileset, 'Layers', '_alllayers', "L#{'%02i' % coord.zoom}", "R#{encode_hex(coord.row)}", "C#{encode_hex(coord.column)}.#{format}")
16
10
 
17
11
  return nil unless File.exists?(path)
18
12
 
19
- if File.exists?(path)
20
- data = File.open(path, 'r') do |f|
21
- begin
22
- f.flock(File::LOCK_SH)
23
- f.read
24
- ensure
25
- f.flock(File::LOCK_UN)
26
- end
13
+ data = File.open(path, 'r') do |f|
14
+ begin
15
+ f.flock(File::LOCK_SH)
16
+ f.read
17
+ ensure
18
+ f.flock(File::LOCK_UN)
27
19
  end
28
20
  end
29
21
 
30
22
  mime_type = "image/#{format}"
31
- Tairu::Tile.new(data, mime_type)
23
+ tile = data.nil? ? nil : Tairu::Tile.new(data, mime_type)
24
+ tile
25
+ end
26
+
27
+ def decode_hex(hex_value)
28
+ Integer("0x#{hex_value}")
29
+ end
30
+
31
+ def encode_hex(int_value, length=8)
32
+ length = "%02i" % length
33
+ "%#{length}x" % int_value
32
34
  end
33
35
  end
34
36
  end
35
- end
37
+ end
@@ -4,170 +4,68 @@ require 'zlib'
4
4
  module Tairu
5
5
  module Store
6
6
  class MBTiles
7
- def initialize;end
8
-
9
- def self.inflate(str)
10
- zstream = Zlib::Inflate.new
11
- buf = zstream.inflate(str)
12
- zstream.finish
13
- zstream.close
14
- buf
7
+ def initialize(layer)
8
+ tileset = File.join(File.expand_path(Tairu.layers[layer]['location']), Tairu.layers[layer]['tileset'])
9
+ conn = defined?(JRUBY_VERSION) ? "jdbc:sqlite:#{tileset}" : "sqlite://#{tileset}"
10
+ @db = Sequel.connect(conn, max_connections: 1)
15
11
  end
16
12
 
17
- def self.connection_string(layer)
18
- loc = File.expand_path(Tairu::CONFIG.layers[layer]['location'])
19
- conn = defined?(JRUBY_VERSION) ? "jdbc:sqlite:#{loc}" : "sqlite://#{loc}"
20
- conn
13
+ def flip_y(zoom, y)
14
+ (2 ** zoom - 1) - y
21
15
  end
22
16
 
23
- def self.create(layer, file, name, type, version, description, format, bounds = nil)
24
- unless %w{png jpg}.index format
25
- raise "Format {#{format}} not supported. Must be 'png' or 'jpg' per MBTiles 1.1 spec."
26
- end
27
-
28
- conn = connection_string(layer)
29
- db = Sequel.connect(File.join(conn, file))
30
-
31
- unless db.table_exists?(:metadata)
32
- db.create_table :metadata do
33
- primary_key :name
34
- String :name
35
- String :value
36
- end
37
-
38
- db.add_index :metadata, :name, unique: true
39
- end
40
-
41
- unless db.table_exists?(:tiles)
42
- db.create_table :tiles do
43
- Integer :zoom_level
44
- Integer :tile_column
45
- Integer :tile_row
46
- Blob :tile_data
47
- end
48
-
49
- db.add_index :tiles, [:zoom_level, :tile_column, :tile_row], unique: true
50
- end
51
-
52
- md = db[:metadata]
53
-
54
- md.insert(name: 'name', value: name)
55
- md.insert(name: 'type', value: type)
56
- md.insert(name: 'version', value: version)
57
- md.insert(name: 'description', value: description)
58
- md.insert(name: 'format', value: format)
59
- md.insert(name: 'bounds', value: bounds) unless bounds.nil?
60
-
61
- db.disconnect
62
- end
63
-
64
- def self.exists?(layer, file)
65
- conn = connection_string(layer)
66
- db = Sequel.connect(File.join(conn, file))
67
-
68
- tv = db.tables + db.views
69
-
70
- [:metadata, :tiles].each {|s| return false unless tv.index(s)}
71
-
72
- exists = db[:metadata].first.nil? && db[:tiles].first.nil?
73
-
74
- db.disconnect
75
-
76
- exists
77
- end
78
-
79
- def self.info(layer, file)
80
- file_loc = File.join(File.expand_path(Tairu::CONFIG.layers[layer]['location']), file)
81
- raise "Tileset does not exist." unless exists? file_loc
82
-
83
- conn = connection_string(layer)
84
- db = Sequel.connect(File.join(conn, file))
85
-
86
- info = {}
87
-
88
- %w{name type version description format bounds}.each do |key|
89
- value = db[:metadata].where(name: key).first
90
- info[key] = value[:value] unless value.nil?
91
- end
92
-
93
- db.disconnect
94
-
95
- info
96
- end
97
-
98
- def self.list(layer, file)
99
- conn = connection_string(layer)
100
- db = Sequel.connect(File.join(conn, file))
101
-
102
- tiles = db[:tiles]
103
-
104
- list = []
105
-
106
- tiles.each do |tile|
107
- list << [((2 ** tile[:zoom_level] - 1) - tile[:tile_row]), tile[:tile_column], tile[:zoom_level]]
108
- end
109
-
110
- db.disconnect
111
-
112
- list
113
- end
114
-
115
- def self.get(layer, file, coord, format = nil)
116
- conn = connection_string(layer)
117
- db = Sequel.connect(File.join(conn, file))
118
-
17
+ def get(coord, format=nil)
119
18
  formats = {
120
19
  'png' => 'image/png',
121
20
  'jpg' => 'image/jpg'
122
21
  }
123
22
 
124
- format = db[:metadata].select(:value).where(name: 'format').first
23
+ format = @db[:metadata].select(:value).where(name: 'format').first
125
24
  mime_type = format.nil? ? formats['png'] : formats[format[:value]]
126
25
 
127
- tile_row = (2 ** coord.zoom - 1) - coord.row
128
- tile = db[:tiles].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: tile_row)
26
+ tile = @db[:tiles].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: flip_y(coord.zoom, coord.row))
129
27
 
130
28
  tile_data = tile.first.nil? ? nil : Tairu::Tile.new(tile.first[:tile_data], mime_type)
131
-
132
- db.disconnect
133
-
134
29
  tile_data
135
30
  end
136
31
 
137
- def self.get_grid(layer, file, coord)
138
- conn = connection_string(layer)
139
- db = Sequel.connect(File.join(conn, file))
140
-
141
- tile_row = (2 ** coord.zoom - 1) - coord.row
142
- grid = db[:grids].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: tile_row)
32
+ def get_grid(coord)
33
+ grid = @db[:grids].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: flip_y(coord.zoom, coord.row))
143
34
 
144
- grid_buf = inflate(grid.first[:grid])
145
-
146
- utf_grid = JSON.parse(grid_buf)
35
+ utf_grid = JSON.parse(inflate(grid.first[:grid]))
147
36
  utf_grid[:data] = {}
148
37
 
149
- grid_data = db[:grid_data].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: tile_row)
150
-
38
+ grid_data = @db[:grid_data].where(zoom_level: coord.zoom, tile_column: coord.column, tile_row: tile_row)
39
+
151
40
  grid_data.each do |gd|
152
41
  utf_grid[:data][gd[:key_name]] = JSON.parse(gd[:key_json])
153
42
  end
154
43
 
155
- db.disconnect
156
-
157
44
  utf_grid
158
45
  end
159
46
 
160
- def self.remove(layer, file, coord)
161
- conn = connection_string(layer)
162
- db = Sequel.connect(File.join(conn, file))
163
- db.disconnect
47
+ def get_legend
48
+ @db[:metadata].where(name: 'legend').first
49
+ end
50
+
51
+ def info
52
+ info = {}
53
+
54
+ %w{name type version description format bounds attribution minzoom maxzoom template legend}.each do |key|
55
+ value = @db[:metadata].where(name: key).first
56
+ info[key] = value[:value] unless value.nil?
57
+ end
58
+
59
+ info
164
60
  end
165
61
 
166
- def self.add(layer, file, coord, tile_data)
167
- conn = connection_string(layer)
168
- db = Sequel.connect(File.join(conn, file))
169
- db.disconnect
62
+ def inflate(str)
63
+ zstream = Zlib::Inflate.new
64
+ buf = zstream.inflate(str)
65
+ zstream.finish
66
+ zstream.close
67
+ buf
170
68
  end
171
69
  end
172
70
  end
173
- end
71
+ end
@@ -0,0 +1,36 @@
1
+ module Tairu
2
+ module Store
3
+ class TMS
4
+ def initialize(layer)
5
+ @tileset = File.join(File.expand_path(Tairu.layers[layer]['location']), Tairu.layers[layer]['tileset'])
6
+ end
7
+
8
+ def flip_y(z, y)
9
+ (2 ** z - 1) - y
10
+ end
11
+
12
+ def get(coord, format='png')
13
+ z = "#{coord.zoom}"
14
+ x = "#{coord.column}"
15
+ y = flip_y(coord.zoom, coord.row)
16
+
17
+ path = File.join(@tileset, z, x, "#{y}.#{format}")
18
+
19
+ return nil unless File.exists?(path)
20
+
21
+ data = File.open(path, 'r') do |f|
22
+ begin
23
+ f.flock(File::LOCK_SH)
24
+ f.read
25
+ ensure
26
+ f.flock(File::LOCK_UN)
27
+ end
28
+ end
29
+
30
+ mime_type = "image/#{format}"
31
+ tile = data.nil? ? nil : Tairu::Tile.new(data, mime_type)
32
+ tile
33
+ end
34
+ end
35
+ end
36
+ end
data/lib/tairu/tile.rb CHANGED
File without changes
data/lib/tairu/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tairu
2
- VERSION = '0.9.0'
3
- end
2
+ VERSION = '0.10.0'
3
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tairu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,49 +9,113 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-11 00:00:00.000000000 Z
12
+ date: 2013-04-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sequel
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - ! '>='
19
+ - - '='
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 3.46.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
- - - ! '>='
27
+ - - '='
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: 3.46.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: redis
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: connection_pool
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: multi_json
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.7.2
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.7.2
30
78
  - !ruby/object:Gem::Dependency
31
79
  name: sinatra
32
80
  requirement: !ruby/object:Gem::Requirement
33
81
  none: false
34
82
  requirements:
35
- - - ! '>='
83
+ - - '='
36
84
  - !ruby/object:Gem::Version
37
- version: '0'
85
+ version: 1.4.2
38
86
  type: :runtime
39
87
  prerelease: false
40
88
  version_requirements: !ruby/object:Gem::Requirement
41
89
  none: false
42
90
  requirements:
43
- - - ! '>='
91
+ - - '='
44
92
  - !ruby/object:Gem::Version
45
- version: '0'
93
+ version: 1.4.2
46
94
  - !ruby/object:Gem::Dependency
47
95
  name: puma
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.6.3
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.6.3
110
+ - !ruby/object:Gem::Dependency
111
+ name: rake
48
112
  requirement: !ruby/object:Gem::Requirement
49
113
  none: false
50
114
  requirements:
51
115
  - - ! '>='
52
116
  - !ruby/object:Gem::Version
53
117
  version: '0'
54
- type: :runtime
118
+ type: :development
55
119
  prerelease: false
56
120
  version_requirements: !ruby/object:Gem::Requirement
57
121
  none: false
@@ -60,7 +124,7 @@ dependencies:
60
124
  - !ruby/object:Gem::Version
61
125
  version: '0'
62
126
  - !ruby/object:Gem::Dependency
63
- name: rake
127
+ name: rspec
64
128
  requirement: !ruby/object:Gem::Requirement
65
129
  none: false
66
130
  requirements:
@@ -80,8 +144,7 @@ email:
80
144
  - scooterwadsworth@gmail.com
81
145
  executables:
82
146
  - tairu
83
- extensions:
84
- - ext/mkrf_conf.rb
147
+ extensions: []
85
148
  extra_rdoc_files: []
86
149
  files:
87
150
  - README.md
@@ -89,21 +152,21 @@ files:
89
152
  - bin/tairu
90
153
  - lib/tairu/cache/disk.rb
91
154
  - lib/tairu/cache/memory.rb
155
+ - lib/tairu/cache/redis_cache.rb
92
156
  - lib/tairu/cache.rb
93
157
  - lib/tairu/configuration.rb
94
158
  - lib/tairu/coordinate.rb
95
- - lib/tairu/images/404.png
96
159
  - lib/tairu/server.rb
97
160
  - lib/tairu/store/esri.rb
98
161
  - lib/tairu/store/mbtiles.rb
162
+ - lib/tairu/store/tms.rb
99
163
  - lib/tairu/store.rb
100
164
  - lib/tairu/tile.rb
101
165
  - lib/tairu/version.rb
102
166
  - lib/tairu.rb
103
- - ext/mkrf_conf.rb
104
167
  homepage: https://github.com/scooterw/tairu
105
168
  licenses:
106
- - MIT
169
+ - BSD
107
170
  post_install_message:
108
171
  rdoc_options: []
109
172
  require_paths:
@@ -122,7 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
185
  version: 1.3.6
123
186
  requirements: []
124
187
  rubyforge_project:
125
- rubygems_version: 1.8.24
188
+ rubygems_version: 1.8.23
126
189
  signing_key:
127
190
  specification_version: 3
128
191
  summary: Simple Tile Server for Ruby
data/ext/mkrf_conf.rb DELETED
@@ -1,26 +0,0 @@
1
- require 'rubygems'
2
- require 'rubygems/command.rb'
3
- require 'rubygems/dependency_installer.rb'
4
-
5
- begin
6
- Gem::Command.build_args = ARGV
7
- rescue NoMethodError
8
- end
9
-
10
- inst = Gem::DependencyInstaller.new
11
-
12
- begin
13
- if defined?(JRUBY_VERSION)
14
- inst.install 'jdbc-sqlite3'
15
- else
16
- inst.install 'sqlite3'
17
- end
18
- rescue
19
- exit(1)
20
- end
21
-
22
- p File.expand_path(__FILE__)
23
-
24
- f = File.open(File.join(File.expand_path(File.dirname(__FILE__)), 'Rakefile'), 'w')
25
- f.write("task :default\n")
26
- f.close
Binary file