A pluggable Node.js map tile server.

Related tags

Maps maps tile-server
Overview

TileStrata

NPM version Build Status Coverage Status

TileStrata is a pluggable "slippy map" tile server that emphasizes code-as-configuration. The primary goal is painless extendability. It's clean, highly tested, performant, and integrates seamlessly with an elastic load balancer designed specifically for tile serving: TileStrata Balancer. Also, there's a built-in profiler and dashboard for debugging render times (read more).

$ npm install tilestrata --save

Table of Contents

Introduction

TileStrata consists of five 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();

// define layers
strata.layer('basemap')
    .route('[email protected]')
        .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
        .use(mapnik({
            pathname: '/path/to/map.xml',
            tileSize: 512,
            scale: 2
        }))
    .route('tile.png')
        .use(disk.cache({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

Routing Without Filenames

As of 2.1.0, if you desire a routing scheme that's closer to other tile servers (where there's no filename) like outlined in #21, use the following format when registering routes:

.route('*.png') // /layer/0/0/0.png
.route('*@2x.png') // /layer/0/0/[email protected]

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();
strata.layer('basemap') /* ... */
strata.layer('contours') /* ... */

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

Usage Notes

Fault-Tolerant Initialization

By default, TileStrata will error when initializing if any of the layer handlers fail to initialize. If you would like to ignore errors so that other layers are booted up and available, use the skipFailures option:

var strata = tilestrata({ skipFailures: true });

Metatile-Aware Load Balancing & Layer Sharding

TileStrata >= 2.0.0 supports integration with TileStrata Balancer, an elastic load balancer designed specifically for the nuances of tile serving – particularly metatiles. Generic load balancers have no knowledge of metatiles and thus will naively split tile requests out to multiple servers which leads to redundant rendering (slow and a waste of computing power).

As an added bonus, the balancer does not assume all servers in the pool have the same layers available. The balancer keeps track of the layers provided on each node so it knows where to route. In sum, the overview:

  • Fully elastic w/minimal setup
  • Consistent routing (improves local cache hits)
  • Metatile-aware (prevents redundant rendering)
  • Layer-aware (allows heterogeneous distribution of layers in the cluster)

View TileStrata Balancer Documentation →

Note: One could use cookie-persistence with traditional load balancers, but this forces users onto a single machine (not optimal).

Rebuilding the Tile Cache

If you update your map styles or data, you'll probably want to update your tiles. Rather than dump all 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

Health Checks

TileStrata includes a /health endpoint that will return a 200 OK if it can accept connections. The response will always be JSON. By setting the "healthy" option to a function that accepts a callback you can take it a step further and control the status and data that it returns.

// not healthy
var strata = tilestrata({
    healthy: function(callback) {
        callback(new Error('CPU is too high'), {loadavg: 3});
    }
});

// healthy
var strata = tilestrata({
    healthy: function(callback) {
        callback(null, {loadavg: 1});
    }
});

Profiling / Debugging Performance

Unless the TILESTRATA_NOPROFILE environment variable is set, TileStrata keeps track of basic latency and size information (min, max, avg) for all steps in the tile serving flow for the lifetime of the process. Data is kept for every plugin on every route of every layer and is broken down by zoom level. To access it, visit: /profile in your browser. If this information needs to be kept private, you can set the TILESTRATA_PASSWORD environment variable to a password that TileStrata will prompt for (username is ignored). The page will have tables like the one below:

t.png z1 z2 z3 z4 z5 z6 z7 z8 z9
reqhook#0 errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 45 44 43 46 52 50 58 61 81
dur_min 45 44 42 46 52 50 58 61 81
dur_avg 45 44 42.5 46 52 50 58 61 81
cache#0.get errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 45 45 44 58 47 62 46 60 53
dur_min 45 45 44 58 47 62 46 60 53
dur_avg 45 45 44 58 47 62 46 60 53
hits 0 0 0 0 0 0 0 0 0
misses 1 1 2 1 1 1 1 1 1
provider#0 errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 34 43 96 122 119 108 115 103 129
dur_min 34 43 64 122 119 108 115 103 129
dur_avg 34 43 80 122 119 108 115 103 129
size_samples 1 1 2 1 1 1 1 1 1
size_max 500B 501B 576B 578B 504B 540B 501B 776B 736B
size_min 500B 501B 565B 578B 504B 540B 501B 776B 736B
size_avg 500B 501B 571B 578B 504B 540B 501B 776B 736B
transform#0 errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 32 33 35 61 49 57 53 50 69
dur_min 32 33 34 61 49 57 53 50 69
dur_avg 32 33 34.5 61 49 57 53 50 69
reshook#0 errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 45 43 45 63 63 55 48 60 68
dur_min 45 43 44 63 63 55 48 60 68
dur_avg 45 43 44.5 63 63 55 48 60 68
cache#0.set errors 0 0 0 0 0 0 0 0 0
dur_samples 1 1 2 1 1 1 1 1 1
dur_max 12 13 13 14 27 23 26 29 27
dur_min 12 13 10 14 27 23 26 29 27
dur_avg 12 13 11.5 14 27 23 26 29 27

API Reference

TileServer

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. It returns the http.Server instance.

server.close([callback])

Stops listening for incoming requests on the port. If TileStrata Balancer is configured, it will also proactively notify it so that the node is removed from the pool.

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. This option can also be set to an array of bounding boxes for rare cases when a layer is noncontinuous.
  • 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.uptime()

Returns an object containing "duration" and "start" (both in milliseconds). If the server hasn't started, the result will be null.

server.version

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

TileLayer

layer.route(filename, [options])

Registers a route and returns a TileRequestHandler instance to be configured. Setting filename to something like "*.ext" or "*@2x.ext" will omit the filename from the request and make the tiles available at /{z}/{x}/{y}.ext and /{z}/{x}/{y}@2x.ext, respectively (see #21).

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.

TileRequestHandler

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.

TileRequest

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

If a tile request is in the filenameless format (see here), hasFilename will be false. To illustrate: if the request is to /layer/0/0/[email protected], filename will be set to [email protected] (for compatibility with caches and plugins that expect a filename) and hasFilename will be false.

tile.clone()

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

Writing TileStrata Plugins

All plugins allow optional init and destroy lifecycle methods that will be called at startup and teardown. The first argument will be the TileServer instance, and the second will be the callback.

Writing Request Hooks

A request hook implementation needs one method: reqhook. 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 {
        name: 'myplugin',
        init: function(server, callback) {
            callback(err);
        },
        reqhook: function(server, tile, req, res, callback) {
            callback();
        },
        destroy: function(server, callback) {
            callback(err);
        }
    };
};

Writing Caches

A cache implementation needs two methods: get, set. 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 {
        name: 'myplugin',
        init: function(server, callback) {
            callback(err);
        },
        get: function(server, tile, callback) {
            callback(err, buffer, headers, /* refresh */);
        },
        set: function(server, tile, buffer, headers, callback) {
            callback(err);
        },
        destroy: function(server, callback) {
            callback(err);
        }
    };
};

A special behavior exists for when a cache returns a hit, but wants a new tile to be generated in the background. The use case: you have tile that's old enough it should be regenerated, but it's not old enough to warrant making the user wait for a new tile to be rendered. To accomplish this in a plugin, have get() return true as the fourth argument to the callback.

callback(null, buffer, headers, true);

Writing Providers

Providers are responsible for building tiles. A provider must define a serve method:

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

Writing Transforms

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

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

Writing Response Hooks

A response hook implementation needs one method: reshook. 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 {
        name: 'myplugin',
        init: function(server, callback) {
            callback(err);
        },
        reshook: function(server, tile, req, res, result, callback) {
            callback();
        },
        destroy: function(server, callback) {
            callback(err);
        }
    };
};

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 accommodate this, TileStrata supports arrays:

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

Contributing

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

$ npm test

License

Copyright © 2014–2020 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.

Comments
  • express and middleware on route.

    express and middleware on route.

    I have write a middleware for auth user.I want tiles not to be served if user is not authed (some of them). I am struggled to find a way to do this.If i use mapnik as it is, i know how to do this, before my route function i put my middleware and its all ok. But how i can do the same thing in tilestrata? I ve seen the reqhook, but i cant understand how to do this.

    Can anyone help me? Thanks a lot.

    question 
    opened by kozer 10
  • Option for omitting a filename

    Option for omitting a filename

    Adds the option noFilename to the main tilestrata configuration parameters to allow for the {z}/{x}/{y}.{extension} request format.

    While this is a global setting, I can also see arguments for having it be a route-specific parameter.

    To use it, simply pass {noFilename: true} when initializing the the tileserver, and pass the file extension in place of the filename (.route('png')) .

    Let me know what you think!

    opened by jczaplew 7
  • Dynamic Tiles

    Dynamic Tiles

    Great work on this so far. I am using Mapnik and currently looking for a solution on how to make dynamic tiles based on a query call. Something like take additional GET parameters in the tileserver such as /{z}/{y}/{z}/png?sql=SELECT id,lng,lat,type FROM LOCATIONS and then have a Mapnik OGR layer XML dynamically created to handle those specific tiles.

    Would something like this be possible using tilestrata as is? or something you would consider adding.

    The demand for dynamic tile serving is high and the only one I know who does it is https://github.com/CartoDB/Windshaft but only for postgres (I would like to use Mapniks virtual layer to do non postgres servers) and ArcGIS server (commercial ), both are beasts and hard to grasp.

    requested-plugin 
    opened by BHare1985 6
  • TypeError: Cannot read property 'getTile' of undefined

    TypeError: Cannot read property 'getTile' of undefined

    Hi, I'm trying to pass the layer name dynamically but when trying to run I'm getting an error "TypeError: Cannot read property 'getTile' of undefined".

    const express = require('express');
    const fs = require('fs');
    
    const app = express();
    
    app.get('/', function (req, res, next) {
        res.send('Hello World!');
    });
    
    app.get('/tileserver/:layer/:z/:x/:y/tile.png', function (req, res, next) {
        const layer = req.params.layer;
        const x = req.params.x;
        const y = req.params.y;
        const z = req.params.z;
        
        const mapfile = __dirname + '/maps/' + layer + '.xml';
        const dircache = '../cache/' + layer;
        
        fs.exists(mapfile, function(exists) {
            if (exists) {
    
                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();
    
                strata.layer('basemap')
                    .route('[email protected]')
                        .use(disk.cache({dir: dircache}))
                        .use(mapnik({
                            pathname: mapfile,
                            tileSize: 512,
                            layer: 'all',
                            scale: 2
                        }))
                    .route('tile.png')
                        .use(disk.cache({dir: dircache}))
                        .use(dependency('basemap', '[email protected]'))
                        .use(sharp(function(image, sharp) {
                            return image.resize(256);
                        }));
                strata.listen();
    
                strata.getTile('basemap', 'tile.png', x, y, z, function(err, img, hdr) {
                    if (err) {
                        console.log(err);
                        res.statusCode = 401;
                        res.send('None shall pass');
                    } else {
                        res.writeHead(200, {
                            'Content-Type': 'image/png',
                            'Content-Length': img.length
                        });
                        res.end(img); 
                    }
                });
    
            } else {
                res.statusCode = 401;
                res.send("Mapfile dont exists");
            }
        });
        
    });
    
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });
    
    app.listen(3000);
    
    opened by jvaldezch 5
  • Map extraction

    Map extraction

    Hello,

    Is it possible to request a "map extraction" based on a specific point?

    Let's say I have a lat/long point and want to retrieve from Tilestrata a piece of map in a specific zoom level and with x pixels of width and y pixels of height.

    If it's not ready, can I have some guideline on how to do it, or another suggestions? The goal is to store this piece of map and display it offline when needed, with a marker, preferably all together like an image.

    opened by carrbrpoa 5
  • Don't send a Content-Length header for 204 (No Data) responses.

    Don't send a Content-Length header for 204 (No Data) responses.

    Some clients are quite picky about receiving 204 responses with a content-length header. This patch solves this by explicitly setting the Content-Length header to 0 for such responses (i.e. empty tiles).

    opened by trasch 5
  • Profile reports inconsistent

    Profile reports inconsistent

    When refreshed, the /profile endpoint seems to rotate between three different versions of statistics. To test, simply go to the /profile endpoint, make sure no requests are being made to the tile server, and refresh at least 3 times.

    opened by jczaplew 3
  • Nothing works

    Nothing works

    Is there anyone who was able to run this? Last error:

    stager@StagerServer:~/tilestrata/example$ node index.js 
    /home/stager/node_modules/tilestrata/lib/TileRequestHandler.js:39
    	if (this.provider) throw new Error('There is a provider already registered to this layer');
    	                   ^
    
    Error: There is a provider already registered to this layer
    

    I broke down...

    question 
    opened by VladimirKalachikhin 2
  • Is it possible to serve tiles at {z}/{x}/{y}.{extension}?

    Is it possible to serve tiles at {z}/{x}/{y}.{extension}?

    Tilestrata is great! Just wondering if hosting tiles at {z}/{x}/{y}.{extension} is possible, or if the /filename.png at the end is always necessary. Just trying to keep consistent URLs for map services I am setting up.

    opened by chriswhong 2
  • Possible to specify layers in extra file?

    Possible to specify layers in extra file?

    Is it somehow and easy possible, to specify layers not directly in index.js, but instead in some other file(s), like layer1.js, layer2.js, layerN.js for example? This would ease the use of puppet or any other configuration management system.

    I've used something like:

    layer1.js:

    strata.layer('layer1') 
    [...]
    module.exports = strata.layer('layer1');
    

    index.js:

    require('./layer1.js')
    

    but this doesn't work, it leads to 404 Not Found. (I've to admit, my knowledge regarding nodejs is really limited, but appreciate any pointers. Thanks!)

    opened by geor-g 2
  • Error using the middleware

    Error using the middleware

    This is the error I get:

    [project-path]/node_modules/tilestrata-mapnik/index.js:49
            source.getTile(req.z, req.x, req.y, function(err, buffer, headers) {
                   ^
    TypeError: Cannot call method 'getTile' of undefined
        at Object.serveImage [as serve] ([project-path]/node_modules/tilestrata-mapnik/index.js:49:10)
        at step_runProvider ([project-path]/node_modules/tilestrata/lib/TileRequestHandler.js:187:18)
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:607:21
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:246:17
        at iterate ([project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:146:13)
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:157:25
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:248:21
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:612:34
        at complete ([project-path]/node_modules/tilestrata/lib/TileRequestHandler.js:173:6)
        at [project-path]/node_modules/tilestrata/node_modules/async/lib/async.js:154:25
        at [project-path]/node_modules/tilestrata/lib/TileRequestHandler.js:162:11
        at [project-path]/node_modules/tilestrata-disk/index.js:35:19
        at fs.js:207:20
        at OpenReq.Req.done (/usr/lib/nodejs/graceful-fs/graceful-fs.js:135:5)
        at OpenReq.done (/usr/lib/nodejs/graceful-fs/graceful-fs.js:64:22)
        at Object.oncomplete (fs.js:107:15)
    

    I'm using express.js 4.13.1, tilestrata 1.4.0 and tilestrata-mapnik 0.2.3.

    I'm more or less using the examples from the readme.md on tilestrata.

    I haven't had a chance to examine this myself just yet, but if I remove the use of the middleware and just use strata.listen(), it works just fine.

    bug 
    opened by Zn4rK 2
  • Shape file compatibility

    Shape file compatibility

    It's my first web map tile service and I'm trying to provide tiles from a shape file. As I have been seeing the only way to render a tile with mapnik is using a XML file. But I can't convert my shape file to it as far as I know.

    There is another provider that works with shape files? I can use GeoJSON files too but without PostGIS database. I'm using Express so I can apply middleware if needed.

    Any workaround is appreciated.

    opened by r4ulill0 0
  • Layer name restrictiveness

    Layer name restrictiveness

    In tilestrata/lib/TileLayer.js:7 is this layer name validation:

    	if (!/^[a-zA-Z0-9_\-]+$/.test(name)) {
    		throw new Error('Invalid layer name "' + name + '" (must match /^[a-zA-Z0-9_\-]+$)');
    	}
    

    I wonder if there is a reason that this is so restrictive and doesn't allow for example ',' (comma) or ';' (semicolon)? I think the URI spec allows them, as far as I understand it (?)

    opened by trasch 0
  • Help with mapnik plugin

    Help with mapnik plugin

    Hello!

    I just installed tilestrata and am trying to use it to render maps where it's data comes from Postgis and it's style comes from mapnik XML (generated by Carto in another solution test - by the way, I'm trying to replace gopnik of that solution for tilestrata, because I know node.js better).

    So the server is up (with some warnings), /health is Ok ({"ok":true,"version":"2.1.0","host":"osboxes","uptime":"15 minutes, 20.149 seconds","uptime_s":920.15}) but when I put together a Leaflet application to try the maps generated, it just gives me NOT FOUNDS (404):

    1.png:1 GET http://localhost:9999/basemap/2/0/1.png 404 (Not Found)
    3.png:1 GET http://localhost:9999/basemap/2/0/3.png 404 (Not Found)
    3.png:1 GET http://localhost:9999/basemap/2/1/3.png 404 (Not Found)
    1.png:1 GET http://localhost:9999/basemap/2/1/1.png 404 (Not Found)
    9625.png:1 GET http://localhost:9999/basemap/14/5859/9625.png 404 (Not Found)
    9625.png:1 GET http://localhost:9999/basemap/14/5863/9625.png 404 (Not Found)
    9625.png:1 GET http://localhost:9999/basemap/14/5864/9625.png 404 (Not Found)
    9625.png:1 GET http://localhost:9999/basemap/14/5857/9625.png 404 (Not Found)
    

    and so on. My tilestrata server code:

    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();
    
    // define layers
    strata.layer('basemap')
        .route('*@2x.png')
            .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
            .use(mapnik({
                pathname: '/home/osboxes/dados_container_renderer/stylesheet_localhost_pg.xml',
                tileSize: 512,
                scale: 2
            }))
        .route('*.png')
            .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
            .use(dependency('basemap', '[email protected]'))
            .use(sharp(function(image, sharp) {
                return image.resize(256);
            }));
    
    // start accepting requests
    strata.listen(9999);
    

    It seems that it's not even scratching server, since no terminal entries are being generated. Am I missing something?

    Thanks a lot!

    opened by carrbrpoa 7
  • Why status code 500 for errors in user supplied health checks?

    Why status code 500 for errors in user supplied health checks?

    I'm playing with health checks currently, and tried this code (from the README):

    var strata = tilestrata({
        healthy: function(callback) {
            callback(new Error('CPU is too high'), {loadavg: 3});
        }
    });
    

    This returns an HTTP code 500, which I would expect only if my request crashed the server. Also, the returned JSON already includes a hint that something is wrong ("ok":false). I wonder, what was the reasoning to set the status code to 500?

    opened by trasch 0
Releases(v2.2.0)
  • v2.2.0(Oct 3, 2020)

  • v2.1.2(Oct 10, 2017)

  • v2.1.1(Oct 10, 2017)

  • v2.1.0(Sep 11, 2017)

  • v2.0.6(Jun 30, 2017)

    • Bugfix: Don't coalesce requests with different query strings https://github.com/naturalatlas/tilestrata/commit/da3cb2d559faf3f029020901319fa5d72e4ebac1. This likely isn't affecting 99% of users... it's more of an issue when building custom functionality that depends on query strings (e.g. tilestrata-postgis-geojson-tiles)
    Source code(tar.gz)
    Source code(zip)
  • v2.0.5(Apr 17, 2017)

    • Bugfix: Fixed a bug that caused hanging requests / a memory leak when requesting tiles with X-TileStrata-SkipCache: 1 header when there are no caches configured for the layer (https://github.com/naturalatlas/tilestrata/commit/dfaaba2cfd24406c51ab976a66a5d34b29acd2be)
    Source code(tar.gz)
    Source code(zip)
  • v2.0.4(Jan 5, 2017)

    • Bugfix: Don't batch requests with differing X-TileStrata-SkipCache headers (race condition). https://github.com/naturalatlas/tilestrata/commit/0a2647060ae88698e5b4756e7f92d21a83091ede
    • Added machine-consumable uptime_s to /health (https://github.com/naturalatlas/tilestrata/pull/12, thanks @trasch)
    Source code(tar.gz)
    Source code(zip)
  • v2.0.2(Apr 11, 2016)

  • v2.0.1(Oct 15, 2015)

    • Fix server crash edge case that occurred if valid health checks comes in from TileStrata Balancer after calling TileStrata instance close() [https://github.com/naturalatlas/tilestrata/commit/dde2dc8f9c8da32da2c7601f26964650007d5a65]
    Source code(tar.gz)
    Source code(zip)
  • v2.0.0(Oct 11, 2015)

    • TileStrata Balancer integration.
      • Smart about metatiles
      • Elastic (nodes enter and leave the pool dynamically)
      • Hash-ring per layer (layers can be spread non-homogeneously about the cluster)
    • Drastically improved readability / styling of profiling dashboard at /profile.
    • Changed construction style from tilestrata.createServer() to tilestrata(opts). If you have custom health check behavior defined, it will need to be updated (read more). The createServer() method still works but is deprecated.
    • Added close() method.
    • Added uptime() method.
    • The listen() method now returns the underlying http.Server instance. Note: To close the port, use close() on the TileStrata instance instead of the http.Server instance.
    • Plugin Changes:
      • Added "name" support (appears on the profiling dashboard)
      • Added "destroy" lifecycle hook. Behaves just like "init", but is called when the server is shutting down (i.e. when the close() method is called).
    • Removed built-in support of ETags. This should now be accomplished by tilestrata-etag, which is more configurable and less-dumb when working with large buffers.
    • Improved logging
    Source code(tar.gz)
    Source code(zip)
  • v1.9.0(Sep 12, 2015)

  • v1.8.0(Sep 8, 2015)

  • v1.7.0(Aug 29, 2015)

  • v1.6.0(Aug 29, 2015)

    • Added new caching behavior that allows a cache to return a hit, while telling tilestrata it should regenerate a tile in the background for the next person. The use case: you have tile that's old enough it should be regenerated, but it's not old enough to warrant making the user wait for a new tile to be rendered. To accomplish this in a plugin, have get() return true as the fourth argument to the callback. Read more about it in the readme.
    Source code(tar.gz)
    Source code(zip)
  • v1.5.2(Aug 22, 2015)

    • TileStrata will now ignore any falsy values passed to .use() instead of throwing an error. This makes conditional plugins in configs a lot cleaner / simpler:
    .layer('mylayer').route('t.png').use(cacheEnabled && disk.cache(...))
    .layer('mylayer').route('t.png').use(null)
    .layer('mylayer').route('t.png').use()
    // etc
    
    Source code(tar.gz)
    Source code(zip)
  • v1.5.1(Aug 22, 2015)

  • v1.5.0(Aug 22, 2015)

  • v1.4.2(Aug 16, 2015)

    • Bugfix: When calling listen() without a callback, errors won't be silenced now (#6)
    • Made providers able to set the HTTP status code used when an error happens.
    Source code(tar.gz)
    Source code(zip)
  • v1.4.1(Jul 31, 2015)

  • v1.4.0(Jul 31, 2015)

    • Added support for X-TileStrata-CacheWait request header. Sending it with a request will ensure that all caches have been written before responding. This is useful for cache invalidators when one tile might depend on another being fully updated first.
    Source code(tar.gz)
    Source code(zip)
  • v1.3.1(Jun 24, 2015)

  • v1.3.0(Jun 20, 2015)

    • Changed behavior of X-TileStrata-CacheSkip header. Now the value is expected to be in the format of [layer]/[file],[layer]/[file],.... This allows for granular control of what's being rebuilt when using tilestrata-dependency (0.4.0 required).
    • Upgraded etag library.
    Source code(tar.gz)
    Source code(zip)
  • v1.2.0(Jun 7, 2015)

  • v1.1.0(Jun 5, 2015)

    • Added: Introduced layer options. As of this release, there's a single "bbox" option that defines the extent of the layer. Any requests outside of this region will yield a 404 Not Found without touching any of the caches, providers, etc.
    .layer('mylayer', {bbox: [
        -111.37390136718749,
        45.3297027614069,
        -111.11228942871094,
        45.47746617959318
    ]})
    
    Source code(tar.gz)
    Source code(zip)
  • v1.0.2(May 12, 2015)

  • v1.0.1(May 12, 2015)

    • Bugfix: Fixed potential race condition. Response hooks that mutate headers could inadvertently affect the headers that end up being cached.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0(May 12, 2015)

    • Addition: Serve "robots.txt" so that search engines don't pick up tiles for image search.
    • Addition: Added "qs" (query string) property to TileRequest.
    • Change: Changed response hooks to be able to modify the response's headers, buffer, and status.
    • Change: Plugins are now implemented via a single use() method instead of distinct registerProvider, registerCache, registerRequestHook, etc methods.

    Breaking Changes

    • Response hooks now must provide a reshook property instead of hook.
    • Request hooks now must provide a reqhook property instead of hook.
    • Response hooks now receive the response buffer, headers, and status bundled up in an object result. With this style, any of those properties can be overridden easily by changing them in the object.

    Chaining Syntax: Almost all of the API has changed to follow a chaining syntax. The purpose of the change is to make layer configuration files much more readable and less verbose.

    // old way
    server.registerLayer(function(layer) {
       layer.setName('mylayer');
       layer.registerRoute('t.png', function(handler) {
           handler.registerCache(disk({...}));
           handler.registerProvider(mapnik({...}));
       });
       layer.registerRoute('[email protected]', function(handler) {
           handler.registerCache(disk({...}));
           handler.registerProvider(mapnik({...}));
       });
    });
    
    // new way
    server.layer('mylayer')
       .route('t.png')
           .use(disk({...}))
           .use(mapnik({...}))
       .route('[email protected]')
           .use(disk({...}))
           .use(mapnik({...}));
    
    Source code(tar.gz)
    Source code(zip)
  • v0.7.1(Apr 22, 2015)

  • v0.7.0(Mar 6, 2015)

  • v0.6.1(Feb 18, 2015)

    • Fixed default value for method on TileRequest objects. This fix is only precautionary – the problem being addressed shouldn't affect normal functioning of TileStrata.
    Source code(tar.gz)
    Source code(zip)
Owner
Natural Atlas
Plan and record your next outdoor adventure using Natural Atlas – online and in your pocket with the Natural Atlas iOS App.
Natural Atlas
Serverless raster and vector map tile generation using Mapnik and AWS Lambda

tilegarden ??️ ?? Contents About Usage Deployment to AWS Additional Configuration Options Required AWS Permissions Features Configuration Selection an

Azavea 89 Dec 22, 2022
Utility to warm up your tile server cache

TileMantle A tool to warm up your tile server cache. Give it a URL template, geometry, and list of zoom levels and it will request tiles incrementally

Natural Atlas 34 Sep 12, 2022
This map is tracking the position of ISS(international space setallite) at every 1 second. I use Nasa's "where the iss" API and "Leaflet.js" for the map.

ISS-tracking-map About This map is tracking the position of ISS(international space setallite) at every 1 second. I use Nasa's "where the iss" API and

Waz.sheeran 2 Oct 25, 2021
Lightweight Node.js isochrone map server

Galton Lightweight Node.js isochrone server. Build isochrones using OSRM, Turf and concaveman. Francis Galton is the author of the first known isochro

Urbica 266 Dec 17, 2022
jQuery Vector Map Library

This project is a heavily modified version of jVectorMap as it was in April of 2012. I chose to start fresh rather than fork their project as my inten

10 Best Design 1.8k Dec 28, 2022
jQuery Vector Map Library

This project is a heavily modified version of jVectorMap as it was in April of 2012. I chose to start fresh rather than fork their project as my inten

10 Best Design 1.8k Dec 28, 2022
JavaScript WebGL 3D map rendering engine

VTS Browser JS is a powerful JavaScript 3D map rendering engine with a very small footprint (about 163 kB of gziped JS code). It provides almost all f

Melown Technologies, SE 203 Dec 7, 2022
Mapbox Visual for Power BI - High performance, custom map visuals for Power BI dashboards

Mapbox Visual for Microsoft Power BI Make sense of your big & dynamic location data with the Mapbox Visual for Power BI. Quickly design high-performan

Mapbox 121 Nov 22, 2022
Add time dimension capabilities on a Leaflet map.

Leaflet TimeDimension Add time dimension capabilities on a Leaflet map. Examples and basic usage API L.Map L.TimeDimension L.TimeDimension.Layer L.Tim

SOCIB public code 379 Dec 23, 2022
A map tool with real-time collaboration 🗺️

Mapus Maps with real-time collaboration ??️ Mapus is a tool to explore and annotate collaboratively on a map. You can draw, add markers, lines, areas,

Alyssa X 3k Jan 4, 2023
3D web map rendering engine written in TypeScript using three.js

3D web map rendering engine written in TypeScript using three.js

HERE Technologies 1.2k Dec 30, 2022
Mind elixir is a free open source mind map core.

Mind-elixir is a framework agnostic mind map core

DJJo 1.4k Jan 2, 2023
Fast Map built for keys that are always fixed size uniformly distributed buffers.

turbo-hash-map Fast Map built for keys that are always fixed size uniformly distributed buffers. npm install turbo-hash-map Uses a prefix trie to map

Mathias Buus 39 Jun 20, 2022
A library that makes Image Map Area responsive

A library that makes Image Map Area responsive

elenh 1 Jan 21, 2022
Greasemonkey script to allow marking items on the interactive map of Elden Ring as completed.

Greasemonkey script (or Tampermonkey) to allow marking items on the interactive map of Elden Ring as completed. The interactive map is a Fextralife-project, all credits for the map go to them.

Daniel Tischner 6 Jun 19, 2022
Interactive map overlay for finding secrets hidden around the world of Lost Ark.

Lostark Map Overlay This is an interactive map overlay which is resizable, movable and can be kept up during gameplay for finding secrets hidden aroun

Omar Minaya 6 Dec 29, 2022
:ukraine: A self-hosted app for keeping track of employee wellbeing and dislocation during the Russo-Ukrainian war, with an interactive map.

Helping organizations stay together and help their members in times of disaster On February 24th, 2022, the lives of the entire Ukrainian nation were

MacPaw Inc. 111 Dec 15, 2022
Generates an embeddable map that displays business info from an OSM object id.

# OSM Business Card Generates an embeddable map that displays business info from an OSM object id. Loads object type (n/w/r) and id from url parameter

Will Bennett 6 May 26, 2022
Simple location picker on Leaflet map

Leaflet Location Picker Simple location picker with Leaflet map Usage: <label>Insert a Geo Location <input id="geoloc" type="text" value="" /> </lab

Stefano Cudini 37 Nov 17, 2022