Skip to content

karelcallens/tilestrata

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

72 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TileStrata

NPM version Build Status Coverage Status

TileStrata is a pluggable "slippy map" tile server that emphasizes code-as-configuration. It's clean, highly tested, and performant. After using TileStache (excellent) we decided we needed something that more-closely matched our stack: Node.js. The primary goal is painless extendability.

$ npm install tilestrata --save

Introduction

TileStrata consists of three main actors, usually implemented as plugins:

  • "provider" – Generates a new tile (e.g mapnik)
  • "cache" – Persists a tile for later requests (e.g. filesystem)
  • "transform" – Takes a raw tile and transforms it (e.g. image scaling / compression)
  • "request hook" – Called at the very beginning of a tile request.
  • "response hook" – Called right before a tile is served to the client.

List of Plugins

Configuration

var tilestrata = require('tilestrata');
var disk = require('tilestrata-disk');
var sharp = require('tilestrata-sharp');
var mapnik = require('tilestrata-mapnik');
var dependency = require('tilestrata-dependency');
var strata = tilestrata.createServer();

// define layers
strata.layer('basemap')
    .route('[email protected]')
        .use(disk({dir: '/var/lib/tiles/basemap'}))
        .use(mapnik({
            xml: '/path/to/map.xml',
            tileSize: 512,
            scale: 2
        }))
    .route('tile.png')
        .use(disk({dir: '/var/lib/tiles/basemap'}))
        .use(dependency('basemap', '[email protected]'))
        .use(sharp(function(image, sharp) {
            return image.resize(256);
        }));

// start accepting requests
strata.listen(8080);

Once configured and started, tiles can be accessed via:

/:layer/:z/:x:/:y/:filename

Integrate with Express.js / Connect

TileStrata comes with middleware for Express that makes serving tiles from an existing application really simple, eliminating the need to call listen on strata.

var tilestrata = require('tilestrata');
var strata = tilestrata.createServer();
strata.layer('basemap') /* ... */
strata.layer('contours') /* ... */

app.use(tilestrata.middleware({
    server: strata,
    prefix: '/maps'
}));

Usage Notes

Rebuilding the Tile Cache

If you update your map styles or data, you'll probably want to update your tiles. Rather than dump of them at once and bring your tile server to a crawl, progressively rebuild the cache by requesting tiles with the X-TileStrata-SkipCache header. TileMantle makes this process easy:

npm install -g tilemantle
tilemantle http://myhost.com/mylayer/{z}/{x}/{y}/t.png \
    -p 44.9457507,-109.5939822 -b 30mi -z 10-14 \
    -H "X-TileStrata-SkipCache:mylayer/t.png"

For the sake of the tilestrata-dependency plugin, the value of the header is expected to be in the format:

X-TileStrata-SkipCache:*
X-TileStrata-SkipCache:[layer]/[file],[layer]/[file],...

In advanced use cases, it might be necessary for tiles to not be returned by the server until the cache is actually written (particularly when order matters due to dependencies). To achieve this, use:

X-TileStrata-CacheWait:1

API Reference

server.listen(port, [hostname], [callback])

Starts accepting requests on the specified port. The arguments to this method are exactly identical to node's http.Server listen() method.

server.layer(name, [opts])

Registers a new layer with the given name and returns its TileLayer instance. If the layer already exists, the existing instance will be returned. Whatever name is used will be the first part of the url that can be used to fetch tiles: /:layer/.... The following options can be provided:

  • bbox: A bounding box (GeoJSON "bbox" format) that defines the valid extent of the layer. Any requests for tiles outside of this region will result in a 404 Not Found.
  • minZoom: The minimum z to return tiles for. Anything lesser will return a 404 Not Found.
  • maxZoom: The maximum z to return tiles for. Anything greater will return a 404 Not Found.
server.getTile(layer, filename, x, y, z, callback)

Attempts to retrieve a tile from the specified layer (string). The callback will be invoked with three arguments: err, buffer, and headers.

server.version

The version of TileStrata (useful to plugins, mainly).

layer.route(filename, [options])

Registers a route. Returns a TileRequestHandler instance to be configured. The available options are:

  • cacheFetchMode: Defines how cache fetching happens when multiple caches are configured. The mode can be "sequential" or "race". If set to "race", TileStrata will fetch from all caches simultaneously and return the first that wins.
handler.use(plugin)

Registers a plugin, which is either a provider, cache, transform, request hook, response hook, or combination of them. See the READMEs on the prebuilt plugins and/or the "Writing TileStrata Plugins" section below for more info.

A request contains these properties: x, y, z, layer (string), filename, method, headers, and qs.

tile.clone()

Returns an identical copy of the tile request that's safe to mutate.

Writing TileStrata Plugins

Writing Request Hooks

A request hook implementation needs one method: reqhook. Optionally it can include an init method that gets called when the server is initializing. The hook's "req" will be a http.IncomingMessage and "res" will be the http.ServerResponse. This makes it possible to respond without even getting to the tile-serving logic (just don't call the callback).

module.exports = function(options) {
    return {
        init: function(server, callback) {
            callback(err);
        },
        reqhook: function(server, tile, req, res, callback) {
            callback();
        }
    };
};

Writing Caches

A cache implementation needs two methods: get, set. Optionally a cache can declare an init method that gets called when the server is initializing. If a cache fails (returns an error to the callback), the server will ignore the error and attempt to serve the tile from the registered provider.

module.exports = function(options) {
    return {
        init: function(server, callback) {
            callback(err);
        },
        get: function(server, tile, callback) {
            callback(err, buffer, headers);
        },
        set: function(server, tile, buffer, headers, callback) {
            callback(err);
        }
    };
};

Writing Providers

Providers are responsible for building tiles. A provider must define a serve method and optionally an init method that is called when the server starts.

module.exports = function(options) {
    return {
        init: function(server, callback) {
            callback(err);
        },
        serve: function(server, tile, callback) {
            callback(err, buffer, headers);
        }
    };
};

Writing Transforms

Transforms modify the result from a provider before it's served (and cached). A tranform must define a transform method and optionally an init method.

module.exports = function(options) {
    return {
        init: function(server, callback) {
            callback(err);
        },
        transform: function(server, tile, buffer, headers, callback) {
            callback(err, buffer, headers);
        }
    };
};

Writing Response Hooks

A response hook implementation needs one method: reshook. Optionally it can include an init method that gets called when the server is initializing. The hook's "req" will be a http.IncomingMessage and "res" will be the http.ServerResponse. The "result" argument contains three properties: headers, buffer, and status — each of which can be modified to affect the final response.

module.exports = function(options) {
    return {
        init: function(server, callback) {
            callback(err);
        },
        reshook: function(server, tile, req, res, result, callback) {
            callback();
        }
    };
};

Multi-Function Plugins

Sometimes a plugin must consist of multiple parts. For instance, a plugin tracking response times must register a request hook and response hook. To accomodate this, TileStrata supports arrays:

module.exports = function() {
    return [
        {reqhook: function(...) { /* ... */ }},
        {reshook: function(...) { /* ... */ }}
    ];
};

Contributing

Before submitting pull requests, please update the tests and make sure they all pass.

$ npm test

License

Copyright © 2014–2015 Natural Atlas, Inc. & Contributors

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

About

A pluggable Node.js map tile server.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 98.9%
  • Makefile 1.1%