mongotiles
Version:
mongotiles is a tilelive backend plug-in for MongoDB GridFS
380 lines (344 loc) • 11.2 kB
JavaScript
// Generated by CoffeeScript 1.10.0
/*
*
* Copyright (C) 2013-2016 by Vaughn Iverson
*
* mongotiles
*
* With this you can use MongoDB GridFS as a "tilelive.js" source or sink for
* map tile / grid / tilejson data.
*
* See: https://github.com/mapbox/tilelive.js
*
* This project is free software released under the MIT/X11 license.
* See LICENSE file for more information.
*
*/
(function() {
var Lock, LockCollection, Tilemongo, default_root, fs, get_mime_type, grid_name, mongodb, path, protocol, querystring, tile_name, tilejson_name, url;
path = require('path');
fs = require('fs');
url = require('url');
querystring = require('querystring');
mongodb = require('mongodb');
LockCollection = require('gridfs-locks').LockCollection;
Lock = require('gridfs-locks').Lock;
protocol = 'mongotiles';
default_root = 'fs';
tile_name = function(z, x, y) {
var name;
if ((x != null) && (y != null) && (z != null)) {
return name = "tile_" + z + "_" + x + "_" + y;
} else {
return name = 'tile_{z}_{x}_{y}';
}
};
grid_name = function(z, x, y) {
var name;
if ((x != null) && (y != null) && (z != null)) {
return name = "grid_" + z + "_" + x + "_" + y;
} else {
return name = 'grid_{z}_{x}_{y}';
}
};
tilejson_name = "tilejson.json";
get_mime_type = function(bytes) {
if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4E && bytes[3] === 0x47 && bytes[4] === 0x0D && bytes[5] === 0x0A && bytes[6] === 0x1A && bytes[7] === 0x0A) {
return 'image/png';
} else if (bytes[0] === 0xFF && bytes[1] === 0xD8 && bytes[bytes.length - 2] === 0xFF && bytes[bytes.length - 1] === 0xD9) {
return 'image/jpeg';
} else if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38 && (bytes[4] === 0x39 || bytes[4] === 0x37) && bytes[5] === 0x61) {
return 'image/gif';
} else {
console.warn(protocol + ": Image data with unknown MIME type in putTile call to get_mime_type.");
return 'application/octet-stream';
}
};
Tilemongo = (function() {
Tilemongo.prototype._file_exists = function(fn, cb) {
return this.files.findOne({
filename: fn
}, {
_id: 1
}, (function(_this) {
return function(err, doc) {
return cb(err, doc);
};
})(this));
};
Tilemongo.prototype._write_buffer = function(fn, type, buffer, metadata, cb) {
var write_it;
write_it = (function(_this) {
return function(_id, l) {
var gs;
gs = new mongodb.GridStore(_this.db, _id, fn, 'w', {
w: 1,
root: _this.grid_root,
content_type: type,
metadata: metadata
});
return gs.open(function(err, gs) {
if (err) {
return cb(err);
}
return gs.write(buffer, function(err, gs) {
if (err) {
return cb(err);
}
return gs.close(function(err) {
if (l) {
l.releaseLock();
}
return cb(err);
});
});
});
};
})(this);
return this._file_exists(fn, (function(_this) {
return function(err, doc) {
var lock;
if (err) {
return cb(err);
}
if (!doc) {
doc = {
_id: new mongodb.ObjectID()
};
}
if (_this.lockColl) {
lock = Lock(doc._id, _this.lockColl, {}).obtainWriteLock();
lock.on('timed-out', function() {
return cb(new Error("Timed out waiting for write lock"));
});
lock.on('error', function(err) {
return cb(err);
});
return lock.on('locked', function(ld) {
return write_it(doc._id, lock);
});
} else {
return write_it(doc._id);
}
};
})(this));
};
Tilemongo.prototype._read_buffer = function(fn, cb) {
var read_it;
read_it = (function(_this) {
return function(_id, l) {
var gs;
gs = new mongodb.GridStore(_this.db, _id, 'r', {
root: _this.grid_root
});
return gs.open(function(err, gs) {
if (err) {
return cb(err);
}
return gs.read(function(err, buffer) {
if (err) {
return cb(err);
}
return gs.close(function(err) {
if (l) {
l.releaseLock();
}
return cb(err, buffer);
});
});
});
};
})(this);
return this._file_exists(fn, (function(_this) {
return function(err, doc) {
var lock;
if (err) {
return cb(err);
}
if (!doc) {
return cb(null, null);
}
if (_this.lockColl) {
lock = Lock(doc._id, _this.lockColl, {}).obtainReadLock();
lock.on('timed-out', function() {
return cb(new Error("Timed out waiting for write lock"));
});
lock.on('error', function(err) {
return cb(err);
});
return lock.on('locked', function(ld) {
return read_it(doc._id, lock);
});
} else {
return read_it(doc._id);
}
};
})(this));
};
Tilemongo.registerProtocols = function(tilelive) {
return tilelive.protocols[protocol + ":"] = this;
};
Tilemongo.list = function(filepath, callback) {
return callback(new Error(".list not implemented for " + protocol));
};
Tilemongo.findID = function(filepath, id, callback) {
return callback(new Error(".findID not implemented for " + protocol));
};
function Tilemongo(uri, callback) {
var locking, ref, ref1, ref2, tilepath_match;
this.starts = 0;
if (typeof uri === 'string') {
uri = url.parse(uri, true);
}
if (typeof uri.query === 'string') {
uri.query = querystring.parse(uri.query);
}
if (uri.protocol !== (protocol + ":")) {
return callback(new Error("Bad uri protocol '" + uri.protocol + "'. Must be " + protocol + "."));
}
tilepath_match = uri.pathname.match(new RegExp("(/[^/]+)(/[^/]+)?"));
if (!tilepath_match) {
return callback(new Error("Bad tile url path '" + uri.pathname + "' for " + uri.protocol + "."));
}
locking = (ref = (ref1 = uri.query) != null ? ref1.locking : void 0) != null ? ref : false;
uri.query = '';
uri.search = '';
uri.hash = '';
uri.protocol = 'http:';
this.source = url.format(uri);
this.db_name = tilepath_match[1].slice(1);
this.grid_root = ((ref2 = tilepath_match[2]) != null ? ref2.slice(1) : void 0) || default_root;
uri.path = this.db_name;
uri.pathname = uri.path;
uri.protocol = 'mongodb:';
this.server = url.format(uri);
console.log("Server: " + this.server);
mongodb.MongoClient.connect(this.server, (function(_this) {
return function(err, db) {
if (err) {
return callback(err);
}
_this.db = db;
console.log("GRID ROOT " + _this.grid_root);
return _this.db.collection(_this.grid_root + ".files", {
w: 1
}, function(err, coll) {
if (err) {
return callback(err);
}
_this.files = coll;
if (!locking) {
return callback(null, _this);
} else {
_this.lockColl = LockCollection(_this.db, {
root: _this.grid_root,
timeOut: 60,
pollingInterval: 5,
lockExpiration: 30
});
_this.lockColl.on('ready', function() {
return callback(null, _this);
});
return _this.lockColl.on('error', function(err) {
return callback(err);
});
}
});
};
})(this));
}
Tilemongo.prototype.close = function(callback) {
return this.db.close(callback);
};
Tilemongo.prototype.getInfo = function(callback) {
return this._read_buffer(tilejson_name, (function(_this) {
return function(err, info) {
if (err) {
return callback(err);
}
return callback(null, JSON.parse(info));
};
})(this));
};
Tilemongo.prototype.getTile = function(z, x, y, callback) {
var tn;
tn = tile_name(z, x, y);
return this._read_buffer(tn, (function(_this) {
return function(err, data) {
if (err) {
return callback(err);
} else if (!data) {
return callback(new Error('Tile does not exist'));
} else {
return callback(null, data);
}
};
})(this));
};
Tilemongo.prototype.getGrid = function(z, x, y, callback) {
var gn;
gn = grid_name(z, x, y);
return this._read_buffer(gn, (function(_this) {
return function(err, data) {
if (err) {
return callback(err);
} else if (!data) {
return callback(new Error('Grid does not exist'));
} else {
return callback(null, JSON.parse(data));
}
};
})(this));
};
Tilemongo.prototype.startWriting = function(callback) {
this.starts += 1;
return callback(null);
};
Tilemongo.prototype.stopWriting = function(callback) {
this.starts -= 1;
return callback(null);
};
Tilemongo.prototype.putInfo = function(info, callback) {
var gn, tn;
if (!this.starts) {
return callback(new Error("Error, writing not started."));
}
tn = tile_name();
info.tiles = ["" + this.source + tn];
if (info.grids != null) {
gn = grid_name();
info.grids = ["" + this.source + gn];
}
return this._write_buffer(tilejson_name, 'application/json', JSON.stringify(info), null, callback);
};
Tilemongo.prototype.putTile = function(z, x, y, tile, callback) {
var tn, type;
if (!this.starts) {
return callback(new Error("Error, writing not started."));
}
tn = tile_name(z, x, y);
type = get_mime_type(tile);
return this._write_buffer(tn, type, tile, {
type: "tile",
x: x,
y: y,
z: z
}, callback);
};
Tilemongo.prototype.putGrid = function(z, x, y, grid, callback) {
var gn;
if (!this.starts) {
return callback(new Error("Error, writing not started."));
}
gn = grid_name(z, x, y);
return this._write_buffer(gn, 'application/json', JSON.stringify(grid), {
type: "grid",
x: x,
y: y,
z: z
}, callback);
};
return Tilemongo;
})();
module.exports = Tilemongo;
}).call(this);