blob: bbfebd37cd25d95f7f44e3db667df6305968ec89 [file]
package filetilestore
import (
"encoding/gob"
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strconv"
"strings"
)
import (
"github.com/golang/glog"
)
import (
"types"
)
// FileTileStore implements TileStore by storing Tiles as gobs in the file system.
//
// The directory structure is dir/datasetName/scale/index.gob where
// index is 0 padded so that the file names sort alphabetically.
type FileTileStore struct {
// The root directory where Tiles should be written.
dir string
// Which dataset are we writing, e.g. "skps" or "micro".
datasetName string
}
func (store FileTileStore) tileFilename(scale, index int) (string, error) {
if scale < 0 || index < 0 {
return "", fmt.Errorf("Scale %d and Index %d must both be > 0", scale, index)
}
return path.Join(store.dir, store.datasetName, fmt.Sprintf("%d/%04d.gob", scale, index)), nil
}
func (store FileTileStore) Put(scale, index int, tile *types.Tile) error {
if index < 0 {
return fmt.Errorf("Can't write Tiles with an index < 0: %d", index)
}
filename, err := store.tileFilename(scale, index)
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return fmt.Errorf("Error creating directory for tile %s: %s", filename, err)
}
f, err := os.Create(filename)
if err != nil {
return fmt.Errorf("Failed to open tile %s for writing: %s", filename, err)
}
defer f.Close()
enc := gob.NewEncoder(f)
if err := enc.Encode(tile); err != nil {
return fmt.Errorf("Failed to encode tile %s: %s", filename, err)
}
return nil
}
func (store FileTileStore) Get(scale, index int) (*types.Tile, error) {
// -1 means find the last tile for the given scale.
if index == -1 {
matches, _ := filepath.Glob(path.Join(store.dir, store.datasetName, fmt.Sprintf("%d/*.gob", scale)))
if matches != nil {
sort.Strings(matches)
lastTileName := filepath.Base(matches[len(matches)-1])
glog.Infof("Found the last tile: %s", lastTileName)
tileIndex := strings.Split(lastTileName, ".")[0]
newIndex, err := strconv.ParseInt(tileIndex, 10, 64)
if err == nil {
index = int(newIndex)
}
} else {
return nil, nil
}
}
filename, err := store.tileFilename(scale, index)
if err != nil {
return nil, err
}
f, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("Failed to open tile %s for reading: %s", filename, err)
}
defer f.Close()
t := types.NewTile()
dec := gob.NewDecoder(f)
if err := dec.Decode(t); err != nil {
return nil, fmt.Errorf("Failed to decode tile %s: %s", filename, err)
}
return t, nil
}
// NewFileTileStore creates a new TileStore that is backed by the file system,
// where dir is the directory name and datasetName is the name of the dataset.
func NewFileTileStore(dir, datasetName string) types.TileStore {
return FileTileStore{
dir: dir,
datasetName: datasetName,
}
}