Skip to content

Commit

Permalink
Merge pull request #107 from DerDakon/no-empty-tiles
Browse files Browse the repository at this point in the history
Use hardlinks for empty bitmap tiles, do not render empty tiles
  • Loading branch information
rurseekatze authored Jun 17, 2016
2 parents d4581a5 + 5a1f60b commit 74c6bb2
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 80 deletions.
104 changes: 56 additions & 48 deletions bitmaptilerequest.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,51 @@ BitmapTilerequest = function(self)

BitmapTilerequest.prototype =
{
// save the rendered image to disk and send it to the client
renderCallback: function(err, image)
{
if (err)
self.tile.debug('Vectortile was empty.');
self.tile.saveBitmapData(image, function(err)
{
if (err)
{
self.response.writeHead(500, {'Content-Type': 'text/plain'});
self.response.end();
self.tile.debug('Empty bitmap tile was responded to the request.');
self.tile.debug('Finished request.');
return;
}

self.tile.getModifyTime(function(err, mtime)
{
var header = self.getHeader();

if (!err)
header['Last-Modified'] = mtime.toUTCString();

self.tile.trace('Responding bitmap data...');
var stream = image.createPNGStream();
self.response.writeHead(200, header);

// write PNG data stream
stream.on('data', function(data)
{
self.response.write(data);
});

// PNG data stream ended
stream.on('end', function()
{
self.response.end();
self.tile.debug('Bitmap tile was responded to the request.');
self.tile.debug('Finished request.');
return;
});
});
});
},

// serves a bitmap tile
getTile: function()
{
Expand Down Expand Up @@ -117,59 +162,23 @@ BitmapTilerequest.prototype =
self.abortRequest('Vectortile could not be created. Aborting.');
return;
}

self.tile.debug('Vector tile created successfully, saving vector tile...');
self.tile.saveVectorData(function(err)
{
if (err)
self.tile.warn('Vector tile could not be saved.');

self.tile.debug('Rendering bitmap tile with style '+self.tile.style);
self.tile.render(function(err, image)
if (self.data.features.length === 0)
{
if (err)
self.tile.debug('Vectortile was empty.');
self.tile.saveBitmapData(image, function(err)
{
if (err)
{
self.response.writeHead(500, {'Content-Type': 'text/plain'});
self.response.end();
self.tile.debug('Empty bitmap tile was responded to the request.');
self.tile.debug('Finished request.');
return;
}

self.tile.getModifyTime(function(err, mtime)
{
var header = {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=3600'
};

if (!err)
header['Last-Modified'] = mtime.toUTCString();

self.tile.trace('Responding bitmap data...');
var stream = image.createPNGStream();
self.response.writeHead(200, header);

// write PNG data stream
stream.on('data', function(data)
{
self.response.write(data);
});

// PNG data stream ended
stream.on('end', function()
{
self.response.end();
self.tile.debug('Bitmap tile was responded to the request.');
self.tile.debug('Finished request.');
return;
});
});
});
});
self.tile.debug('Vector tile without features, serving empty PNG tile for style ' + self.tile.style);
renderCallback(false, null);
}
else
{
self.tile.debug('Rendering bitmap tile with style ' + self.tile.style);
self.tile.render(renderCallback);
}
});
});
}
Expand Down Expand Up @@ -206,8 +215,6 @@ BitmapTilerequest.prototype =

if (expired)
header['Cache-Control'] = 'max-age=0';
else
header['Cache-Control'] = 'public, max-age=3600';

self.tile.trace('Responding bitmap data...');
var stream = image.createPNGStream();
Expand Down Expand Up @@ -250,6 +257,7 @@ BitmapTilerequest.prototype =
{
return {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=3600',
'Server': 'node-tileserver/0.3'
};
}
Expand Down
1 change: 1 addition & 0 deletions emptytile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"features":[],"granularity":10}
74 changes: 42 additions & 32 deletions tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ Tile.prototype =
}

var filepath = configuration.vtiledir+'/'+this.z+'/'+this.x+'/';
var file = this.y+'.json';
var file = filepath + this.y + '.json';

this.debug('Creating path '+filepath+'...');
var self = this;
Expand All @@ -155,19 +155,37 @@ Tile.prototype =
});
}

self.debug('Created path. Saving vector tile at path: '+filepath+file);
fs.writeFile(filepath+file, JSON.stringify(self.data), {mode: 0777}, function(err)
if (self.data.features.length === 0)
{
self.debug('Created path. Linking empty vector tile to: ' + file);
fs.link('emptytile.json', file, function(err)
{
if (!err)
self.debug('Empty vector tile was stored.');
else
self.debug('Could not link empty vector file.');

return process.nextTick(function()
{
callback(true);
});
});
}

self.debug('Created path. Saving vector tile at path: ' + file);
fs.unlinkSync(file);
fs.writeFile(file, JSON.stringify(self.data), {mode: 0666}, function(err)
{
if (err)
{
self.error('Cannot save vector tile at path: '+filepath+file);
self.error('Cannot save vector tile at path: ' + file);
return process.nextTick(function()
{
callback(err);
});
}

self.debug('Saved vector tile at path: '+filepath+file);
self.debug('Saved vector tile at path: ' + file);
return process.nextTick(function()
{
callback(false);
Expand Down Expand Up @@ -428,15 +446,13 @@ Tile.prototype =
client.query(self.getDatabaseQuery(bbox_p), function(err, results)
{
var content = new Object();
content.features = new Array();

self.trace('All database queries finished, generating JSON data object.');
content.features = self.getJSONFeatures(results);

// catch tiles without data
if (!content.features)
if (content.features.length === 0)
{
content.features = new Array();
self.debug('Vector tile contains no data.');
}

Expand Down Expand Up @@ -491,8 +507,10 @@ Tile.prototype =
return;
}

self.debug('Saving bitmap tile at path: '+filepath+'/'+self.y+'.png');
var out = fs.createWriteStream(filepath+'/'+self.y+'.png', {mode: 0777});
var fname = filepath + '/' + self.y + '.png';
self.debug('Saving bitmap tile at path: ' + fname);
fs.unlinkSync(fname)
var out = fs.createWriteStream(fname, {mode: 0666});
var stream = image.createPNGStream();

// write PNG data stream
Expand Down Expand Up @@ -695,10 +713,11 @@ Tile.prototype =
// converts raw JSON features from database response to objects
getJSONFeatures: function(data)
{
var features = new Array();

if (typeof data == undefined || data == null)
return [];
return features;

var features = new Array();
for (var i=0; i<data.rows.length; i++)
{
// catch JSON parsing errors
Expand Down Expand Up @@ -774,40 +793,31 @@ Tile.prototype =
});
}

var fname = filepath + '/' + self.y + '.png';
fs.unlinkSync(fname);

// store empty tile if no image could be rendered
if (image == null)
{
self.debug('Bitmap tile empty.');

fs.readFile('emptytile.png', function(err, data)
fs.link('emptytile.png', fname, function(err)
{
if (err)
{
self.warn('Could not read empty bitmap tile.');
return process.nextTick(function()
{
callback(true);
});
}
if (!err)
self.debug('Empty bitmap tile was stored.');
else
self.debug('Could not link empty bitmap file.');

fs.writeFile(filepath+'/'+self.y+'.png', data, {mode: 0777}, function(err)
return process.nextTick(function()
{
if (!err)
self.debug('Empty bitmap tile was stored.');
else
self.debug('Could not save empty bitmap file.');

return process.nextTick(function()
{
callback(true);
});
callback(true);
});
});
}
else
{
self.trace('Saving bitmap data...');
var out = fs.createWriteStream(filepath+'/'+self.y+'.png', {mode: 0777});
var out = fs.createWriteStream(fname, {mode: 0666});
var stream = image.createPNGStream();

// write PNG data stream
Expand Down

0 comments on commit 74c6bb2

Please sign in to comment.