tairu 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,3 +1,46 @@
1
1
  tairu ... simple map tile server
2
2
 
3
- To play with the library in irb: `rake console`
3
+ In development mode (irb): `rake console`
4
+
5
+ To run: `tairu -c /path/to/config/file`
6
+
7
+ Example config file:
8
+
9
+ ```ruby
10
+
11
+ name: tairu_config_example
12
+ cache:
13
+ type: memory
14
+ layers:
15
+ geo:
16
+ provider: mbtiles
17
+ tileset: geography-class.mbtiles
18
+ location: ~/.tairu/tilesets
19
+ format: png
20
+
21
+ ```
22
+
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):
28
+
29
+ ```ruby
30
+
31
+ layers = {
32
+ 'geo' => {
33
+ 'provider' => 'mbtiles',
34
+ 'tileset' => 'geography-class.mbtiles',
35
+ 'location' => '~/.tairu/tilesets',
36
+ 'format' => 'png'
37
+ }
38
+ }
39
+
40
+ cache = Tairu::Configuration.start_cache('memory', {})
41
+
42
+ name = 'tairu_config_example'
43
+
44
+ Tairu::CONFIG = Tairu::Configuration.new(layers, cache, name)
45
+
46
+ ```
data/bin/tairu CHANGED
@@ -10,6 +10,12 @@ opts = {}
10
10
  OptionParser.new do |o|
11
11
  o.banner = 'Usage: tairu.rb [options]'
12
12
 
13
+ o.on('-v', '--version', '') do
14
+ require 'tairu'
15
+ puts "Tairu v#{Tairu::VERSION}"
16
+ exit(0)
17
+ end
18
+
13
19
  o.on('-c', '--config CONFIG', '') do |config|
14
20
  opts[:config] = File.expand_path(config)
15
21
  end
@@ -26,7 +32,7 @@ end.parse!
26
32
  if opts[:config]
27
33
  require 'tairu'
28
34
 
29
- Tairu::CONFIG = Tairu::Configuration.new(opts[:config])
35
+ Tairu::CONFIG = Tairu::Configuration.config_from_file(opts[:config])
30
36
  Tairu::CACHE = Tairu::CONFIG.cache
31
37
  else
32
38
  puts "No valid config file specified. Use -c / --config option."
@@ -11,14 +11,14 @@ module Tairu
11
11
  TILE_404 = Tairu::Tile.new(File.read(File.join(File.expand_path(File.dirname(__FILE__)), 'tairu', 'images', '404.png')), 'image/png')
12
12
 
13
13
  def self.get_tile(name, coord, format = nil)
14
- tileset = Tairu::CONFIG.data['cache']['layers'][name]
14
+ tileset = Tairu::CONFIG.layers[name]
15
15
 
16
16
  unless tileset.nil?
17
17
  tile = Tairu::CACHE.get(tileset, coord)
18
18
 
19
19
  if tile.nil?
20
20
  provider = Tairu::Store::TYPES[tileset['provider']]
21
- provider_tile = provider.get(name, tileset['tileset'], coord)
21
+ provider_tile = provider.get(name, tileset['tileset'], coord, tileset['format'])
22
22
 
23
23
  unless provider_tile.nil?
24
24
  tile = provider_tile
@@ -1,7 +1,69 @@
1
+ require 'fileutils'
2
+
1
3
  module Tairu
2
4
  module Cache
3
5
  class Disk
4
- def initialize(options = nil)
6
+ def initialize(options = {})
7
+ raise "No path specified." unless options['path']
8
+ @path = options['path']
9
+ @expire = options['expire'] || 300
10
+ @tiles = {}
11
+ end
12
+
13
+ def lock_write(path, data)
14
+ File.open(path, 'wb') do |f|
15
+ begin
16
+ f.flock(File::LOCK_EX)
17
+ f.write(data)
18
+ ensure
19
+ f.flock(File::LOCK_UN)
20
+ end
21
+ end
22
+ end
23
+
24
+ def lock_read(path)
25
+ if File.exists?(path)
26
+ if File.mtime(path) > (Time.now - @expire)
27
+ data = File.open(path, 'rb') do |f|
28
+ begin
29
+ f.flock(File::LOCK_SH)
30
+ f.read
31
+ ensure
32
+ f.flock(File::LOCK_UN)
33
+ end
34
+ end
35
+ else
36
+ FileUtils.rm(path)
37
+ end
38
+ end
39
+
40
+ data
41
+ end
42
+
43
+ def add(layer, coord, tile)
44
+ expire = Time.now + @expire
45
+ tileset = layer['tileset']
46
+ base_path = File.join(File.expand_path(@path), tileset, "#{coord.zoom}", "#{coord.column}")
47
+ FileUtils.mkdir_p(base_path)
48
+ path = File.join(base_path, "#{coord.row}.#{layer['format']}")
49
+ lock_write(path, tile.data)
50
+ purge_expired(layer)
51
+ end
52
+
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']}")
56
+ data = lock_read(path)
57
+
58
+ return nil if data.nil?
59
+
60
+ Tairu::Tile.new(data, layer['format'])
61
+ end
62
+
63
+ def purge_expired(layer)
64
+ Dir.glob(File.join(File.expand_path(@path), "**/*.#{layer['format']}")).each do |f|
65
+ FileUtils.rm(f) if File.mtime(f) < (Time.now - @expire)
66
+ end
5
67
  end
6
68
  end
7
69
  end
@@ -9,7 +9,7 @@ module Tairu
9
9
  key = Tairu::Cache::Key.new(layer, coord)
10
10
  expire = Time.now + age
11
11
  @tiles[key] = {tile: tile, expire: expire}
12
- :purge_expired_tiles
12
+ :purge_expired
13
13
  end
14
14
 
15
15
  def get(layer, coord) # format?
@@ -2,20 +2,48 @@ require 'yaml'
2
2
 
3
3
  module Tairu
4
4
  class Configuration
5
- attr_accessor :data, :cache
5
+ attr_accessor :layers, :cache, :name
6
6
 
7
- def initialize(config = nil)
8
- raise "No config file specified." unless config
9
- @data = YAML.load_file(config)
10
- @cache = start_cache
7
+ def initialize(layers, cache, name = nil)
8
+ @layers = layers
9
+ @cache = cache
10
+ @name = name
11
11
  end
12
12
 
13
- def start_cache
14
- cache_type = @data['cache']['type']
15
- cache_options = @data['cache']['options']
13
+ def self.config_from_file(file)
14
+ file = File.expand_path(file)
16
15
 
16
+ if File.exists?(file)
17
+ data = YAML.load_file(file)
18
+ else
19
+ raise 'Configuration file not found at specified location.'
20
+ end
21
+
22
+ name = data['name'] if data['name']
23
+
24
+ if data['layers']
25
+ layers = data['layers']
26
+ else
27
+ raise 'Layers must be specified.'
28
+ end
29
+
30
+ if data['cache'] && data['cache']['type']
31
+ cache_type = data['cache']['type']
32
+ options = data['cache']['options'] ? data['cache']['options'] : {}
33
+
34
+ cache = start_cache(cache_type, options)
35
+ end
36
+
37
+ cache = Tairu::Cache::Memory.new if cache.nil?
38
+
39
+ config = Configuration.new(layers, cache, name)
40
+
41
+ config
42
+ end
43
+
44
+ def self.start_cache(cache_type = nil, options = nil)
17
45
  if cache_type
18
- cache = Tairu::Cache::TYPES[cache_type].new(cache_options)
46
+ cache = Tairu::Cache::TYPES[cache_type].new(options)
19
47
  else
20
48
  cache = Tairu::Cache::Memory.new
21
49
  end
@@ -5,7 +5,7 @@ module Tairu
5
5
  class Server < Sinatra::Base
6
6
  get '/:tileset/:zoom/:row/:col.grid.json' do
7
7
  coord = Tairu::Coordinate.new(Integer(params[:row]), Integer(params[:col]), Integer(params[:zoom]))
8
- tileset = Tairu::CONFIG.data['cache']['layers'][params[:tileset]]
8
+ tileset = Tairu::CONFIG.layers[params[:tileset]]
9
9
  tile = Tairu::Store::MBTiles.get_grid(params[:tileset], tileset['tileset'], coord)
10
10
 
11
11
  callback = params.delete('callback')
@@ -1,12 +1,3 @@
1
- # Integer("0x0000000a") => 10
2
- # 10.to_s(16) => "a"
3
- # "%x" % 10 => "a"
4
- # "%08x" % 10 => "0000000a"
5
- # "#%02x%02x%02x" % [255, 0, 10] => #ff000a
6
- # /Layers/_alllayers/Lxx/Rhex/Chex.fmt => L15/R0000000a/C0000000a.jpg
7
- # http://services.arcgisonline.com/cache_im/l3_imagery_Prime_world_2D/Layers/_alllayers/L15/R000027f9/C00002d2c.jpg
8
- # http://proceedings.esri.com/library/userconf/devsummit07/papers/building_and_using_arcgis_server_map_caches-best_practices.pdf
9
-
10
1
  module Tairu
11
2
  module Store
12
3
  class Esri
@@ -20,12 +11,22 @@ module Tairu
20
11
  end
21
12
 
22
13
  def self.get(layer, tileset, coord, format = 'png')
23
- loc = File.expand_path(Tairu::CONFIG.data['cache']['layers'][layer]['location'])
14
+ loc = File.expand_path(Tairu::CONFIG.layers[layer]['location'])
24
15
  path = File.join(loc, tileset, 'Layers', '_alllayers', "L#{'%02i' % coord.zoom}", "R#{encode_hex(coord.row)}", "C#{encode_hex(coord.column)}.#{format}")
25
16
 
26
17
  return nil unless File.exists?(path)
27
18
 
28
- data = File.read(path)
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
27
+ end
28
+ end
29
+
29
30
  mime_type = "image/#{format}"
30
31
  Tairu::Tile.new(data, mime_type)
31
32
  end
@@ -15,7 +15,7 @@ module Tairu
15
15
  end
16
16
 
17
17
  def self.connection_string(layer)
18
- loc = File.expand_path(Tairu::CONFIG.data['cache']['layers'][layer]['location'])
18
+ loc = File.expand_path(Tairu::CONFIG.layers[layer]['location'])
19
19
  conn = defined?(JRUBY_VERSION) ? "jdbc:sqlite:#{loc}" : "sqlite://#{loc}"
20
20
  conn
21
21
  end
@@ -77,7 +77,7 @@ module Tairu
77
77
  end
78
78
 
79
79
  def self.info(layer, file)
80
- file_loc = File.join(File.expand_path(Tairu::CONFIG.data['cache']['layers'][layer]['location']), file)
80
+ file_loc = File.join(File.expand_path(Tairu::CONFIG.layers[layer]['location']), file)
81
81
  raise "Tileset does not exist." unless exists? file_loc
82
82
 
83
83
  conn = connection_string(layer)
@@ -112,7 +112,7 @@ module Tairu
112
112
  list
113
113
  end
114
114
 
115
- def self.get(layer, file, coord)
115
+ def self.get(layer, file, coord, format = nil)
116
116
  conn = connection_string(layer)
117
117
  db = Sequel.connect(File.join(conn, file))
118
118
 
@@ -1,3 +1,3 @@
1
1
  module Tairu
2
- VERSION = '0.8.2'
2
+ VERSION = '0.9.0'
3
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.8.2
4
+ version: 0.9.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-10 00:00:00.000000000 Z
12
+ date: 2012-12-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sequel