tairu 0.9.0 → 0.10.0

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