tairu 0.8.2 → 0.9.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
@@ -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