Compare commits

...

22 Commits

Author SHA1 Message Date
Petr Sloup
f1c835c21d Update package version to 1.1.4 2016-10-07 15:21:58 +02:00
Petr Sloup
15ed6d74bf Fix incorrect XYZ URLs (close #58) 2016-10-07 15:21:12 +02:00
Petr Sloup
a3d8240aac Do not add scale prefix when viewing raster data tiles (close #57) 2016-10-07 11:16:22 +02:00
Petr Sloup
098f057e55 Update package version to 1.1.3 2016-09-30 09:51:20 +02:00
Petr Sloup
18ba6f5059 Ignore tiling scheme of the source for internal meta urls (#54) 2016-09-30 09:50:05 +02:00
Petr Sloup
820dbdd3dd Update package version to 1.1.2 2016-09-27 17:34:39 +02:00
Petr Sloup
acd7683f18 Update dependencies 2016-09-27 17:30:24 +02:00
Petr Sloup
09859c10c1 Serve any vector/raster mbtiles, use styles only for osm2vt (close #51, #52) 2016-09-27 17:26:05 +02:00
Petr Sloup
f201deecdc Merge pull request #48 from rastapasta/patch-1
adding info for OSX setup
2016-09-22 08:55:41 +02:00
Petr Pridal
b49521d2ed Update README.md 2016-09-01 17:53:15 +02:00
Michael Straßburger
30c9bc8979 adding info for OSX setup 2016-08-29 12:31:33 +02:00
Petr Sloup
69340bbc83 Update package version to 1.1.1 2016-08-25 12:52:51 +02:00
Petr Sloup
ff9d5a8e5d Update READMEs and descriptions 2016-08-25 12:52:32 +02:00
Petr Sloup
23c2aa54d0 Update tests 2016-08-25 11:09:01 +02:00
Petr Sloup
a8fd1b38b7 Return 404 for negative zoom levels on static endpoint 2016-08-25 11:08:19 +02:00
Petr Sloup
7c28bf2b21 Update version to 1.1.0 2016-08-25 10:49:27 +02:00
Petr Sloup
f9f26f0d65 Indicate clearly when running light version on index.html 2016-08-25 10:44:29 +02:00
Petr Sloup
de60a0a076 Disable png quantization by default for now 2016-08-25 10:23:42 +02:00
Petr Sloup
90b9af3d95 Allow max image side length to be configurable 2016-08-25 09:41:33 +02:00
Petr Sloup
78aea26318 Allow @4x requests 2016-08-25 09:38:03 +02:00
Petr Sloup
292b1b6b44 Update dependencies 2016-08-25 09:28:17 +02:00
Petr Sloup
de7f5f0366 Allow and use floating-point zoom levels in the static endpoints 2016-08-25 09:22:12 +02:00
15 changed files with 160 additions and 60 deletions

View File

@@ -1,18 +1,46 @@
![tileserver-gl](https://cloud.githubusercontent.com/assets/59284/18173467/fa3aa2ca-7069-11e6-86b1-0f1266befeb6.jpeg)
# TileServer GL # TileServer GL
[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl) [![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl)
[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/) [![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/)
Vector and raster maps with GL styles. Server side rendering by Mapbox GL Native. Map tile server for Mapbox GL JS, Android, iOS, Leaflet, OpenLayers, GIS via WMTS, etc.
## Quickstart ## Get Started
Use `npm install -g tileserver-gl` to install the package from npm.
Then you can simply run `tileserver-gl zurich_switzerland.mbtiles` to start the server for the given mbtiles. Install `tileserver-gl` with server-side raster rendering of vector tiles with npm
Alternatively, you can use `tileserver-gl-light` package instead, which is pure javascript (does not have any native dependencies) and can run anywhere, but does not contain rasterization features. ```bash
npm install -g tileserver-gl
```
Or you can use `docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl` to run the server inside a docker container. Now download vector tiles from [OSM2VectorTiles](http://osm2vectortiles.org/downloads/).
Prepared vector tiles can be downloaded from [OSM2VectorTiles](http://osm2vectortiles.org/). ```bash
curl -o zurich_switzerland.mbtiles https://osm2vectortiles-downloads.os.zhdk.cloud.switch.ch/v2.0/extracts/zurich_switzerland.mbtiles
```
Start `tileserver-gl` with the downloaded vector tiles.
```bash
tileserver-gl zurich_switzerland.mbtiles
```
Alternatively, you can use the `tileserver-gl-light` package instead, which is pure javascript (does not have any native dependencies) and can run anywhere, but does not contain rasterization on the server side made with MapBox GL Native.
## Using Docker
An alternative to npm to start the packed software easier is to install [Docker](http://www.docker.com/) on your computer and then run in the directory with the downloaded MBTiles the command:
```bash
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl
```
This will download and start a ready to use container on your computer and the maps are going to be available in webbrowser on localhost:8080.
On laptop you can use [Docker Kitematic](https://kitematic.com/) and search "tileserver-gl" and run it, then drop in the 'data' folder the MBTiles.
## Documentation ## Documentation
You can read full documentation of this project at http://tileserver.readthedocs.io/. You can read full documentation of this project at http://tileserver.readthedocs.io/.

17
README_light.md Normal file
View File

@@ -0,0 +1,17 @@
# TileServer GL light
[![Build Status](https://travis-ci.org/klokantech/tileserver-gl.svg?branch=master)](https://travis-ci.org/klokantech/tileserver-gl)
[![Docker Hub](https://img.shields.io/badge/docker-hub-blue.svg)](https://hub.docker.com/r/klokantech/tileserver-gl/)
Vector maps with GL styles. Map tile server for Mapbox Android, iOS, GL JS, Leaflet, OpenLayers, etc. without server side rendering.
## Quickstart
Use `npm install -g tileserver-gl-light` to install the package from npm.
Then you can simply run `tileserver-gl-light zurich_switzerland.mbtiles` to start the server for the given mbtiles.
See also `tileserver-gl` which contains server side rendering.
Prepared vector tiles can be downloaded from [OSM2VectorTiles](http://osm2vectortiles.org/).
## Documentation
You can read full documentation of this project at http://tileserver.readthedocs.io/.

View File

@@ -20,10 +20,12 @@ Example::
"127.0.0.1:8080" "127.0.0.1:8080"
], ],
"formatQuality": { "formatQuality": {
"png": 90,
"jpeg": 80, "jpeg": 80,
"webp": 90 "webp": 90,
} "pngQuantization": false,
"png": 90
},
"maxSize": 2048
}, },
"styles": { "styles": {
"basic": { "basic": {
@@ -69,6 +71,13 @@ You can use this to optionally specify on what domains the rendered tiles are ac
Quality of the compression of individual image formats. [0-100] Quality of the compression of individual image formats. [0-100]
The value for ``png`` is only used when ``pngQuantization`` is ``true``.
``maxSize``
-----------
Maximum image side length to be allowed to be rendered (including scale factor). Default is ``2048``.
``styles`` ``styles``
========== ==========

View File

@@ -15,7 +15,7 @@ Rendered tiles
============== ==============
* Rendered tiles are served at ``/styles/{id}/rendered/{z}/{x}/{y}[@2x].{format}`` * Rendered tiles are served at ``/styles/{id}/rendered/{z}/{x}/{y}[@2x].{format}``
* The optional ``@2x`` (or ``@3x``) part can be used to render HiDPI (retina) tiles * The optional ``@2x`` (or ``@3x``, ``@4x``) part can be used to render HiDPI (retina) tiles
* Available formats: ``png``, ``jpg`` (``jpeg``), ``webp`` * Available formats: ``png``, ``jpg`` (``jpeg``), ``webp``
* TileJSON at ``/styles/{id}/rendered.json`` * TileJSON at ``/styles/{id}/rendered.json``

View File

@@ -27,3 +27,11 @@ Make sure you have Node v4 or higher (nvm install 4) and run::
npm install npm install
node . node .
On OSX
======
Make sure to have ``pkg-config`` and ``cairo`` installed::
brew install pkg-config cairo

View File

@@ -1,7 +1,7 @@
{ {
"name": "tileserver-gl", "name": "tileserver-gl",
"version": "1.0.0", "version": "1.1.4",
"description": "Map tile server for JSON GL styles - serverside generated raster tiles", "description": "Map tile server for JSON GL styles - vector and server side generated raster tiles",
"main": "src/main.js", "main": "src/main.js",
"bin": "src/main.js", "bin": "src/main.js",
"authors": [ "authors": [
@@ -19,19 +19,19 @@
"async": "2.0.1", "async": "2.0.1",
"advanced-pool": "0.3.2", "advanced-pool": "0.3.2",
"base64url": "2.0.0", "base64url": "2.0.0",
"canvas": "1.4.0", "canvas": "1.5.0",
"clone": "1.0.2", "clone": "1.0.2",
"color": "0.11.3", "color": "0.11.3",
"cors": "2.7.1", "cors": "2.8.1",
"express": "4.14.0", "express": "4.14.0",
"glyph-pbf-composite": "0.0.2", "glyph-pbf-composite": "0.0.2",
"handlebars": "4.0.5", "handlebars": "4.0.5",
"mapbox-gl-native": "3.3.2", "mapbox-gl-native": "3.3.3",
"mbtiles": "0.9.0", "mbtiles": "0.9.0",
"morgan": "1.7.0", "morgan": "1.7.0",
"node-pngquant-native": "1.0.4", "node-pngquant-native": "1.0.4",
"nomnom": "1.8.1", "nomnom": "1.8.1",
"request": "2.74.0", "request": "2.75.0",
"sharp": "0.16.0", "sharp": "0.16.0",
"sphericalmercator": "1.0.5", "sphericalmercator": "1.0.5",
"tileserver-gl-styles": "0.3.0" "tileserver-gl-styles": "0.3.0"

View File

@@ -36,6 +36,16 @@ a:hover{
font-size: 32px; font-size: 32px;
text-align:center; text-align:center;
margin:90px 0 0 0; margin:90px 0 0 0;
position:relative;
}
.title.light:after {
content: "light";
display: block;
position: absolute;
left: 50%;
bottom: -5px;
color: #499DCE;
font-size:.8em;
} }
section{ section{
margin: 15px auto; margin: 15px auto;
@@ -169,6 +179,9 @@ body {
.title{ .title{
margin: 25px 0 0 0; margin: 25px 0 0 0;
} }
.title.light:after {
font-size:.6em;
}
.title img{ .title img{
width: 200px; width: 200px;
} }

View File

@@ -124,6 +124,10 @@
<div id='map'></div> <div id='map'></div>
<script> <script>
var map = L.mapbox.map('map', '/data/{{id}}.json{{&key_query}}', { zoomControl: false }); var map = L.mapbox.map('map', '/data/{{id}}.json{{&key_query}}', { zoomControl: false });
map.eachLayer(function(layer) {
// do not add scale prefix even if retina display is detected
layer.scalePrefix = '.';
});
new L.Control.Zoom({ position: 'topright' }).addTo(map); new L.Control.Zoom({ position: 'topright' }).addTo(map);
setTimeout(function() { setTimeout(function() {
new L.Hash(map); new L.Hash(map);

View File

@@ -17,8 +17,8 @@
</head> </head>
<body> <body>
<section> <section>
<h1 class="title"><img src="/images/logo.png" alt="TileServer GL" /></h1> <h1 class="title {{#if is_light}}light{{/if}}"><img src="/images/logo.png" alt="TileServer GL" /></h1>
<h2 class="subtitle">Vector and raster maps with GL styles</h2> <h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2>
<h2 class="box-header">Styles</h2> <h2 class="box-header">Styles</h2>
<div class="box"> <div class="box">
{{#each styles}} {{#each styles}}

View File

@@ -20,6 +20,7 @@ var fs = require('fs');
var packageJson = require('./package'); var packageJson = require('./package');
packageJson.name += '-light'; packageJson.name += '-light';
packageJson.description = 'Map tile server for JSON GL styles - serving vector tiles';
delete packageJson.dependencies['canvas']; delete packageJson.dependencies['canvas'];
delete packageJson.dependencies['mapbox-gl-native']; delete packageJson.dependencies['mapbox-gl-native'];
delete packageJson.dependencies['node-pngquant-native']; delete packageJson.dependencies['node-pngquant-native'];
@@ -29,6 +30,7 @@ delete packageJson.devDependencies;
var str = JSON.stringify(packageJson, undefined, 2); var str = JSON.stringify(packageJson, undefined, 2);
fs.writeFileSync('light/package.json', str); fs.writeFileSync('light/package.json', str);
fs.renameSync('light/README_light.md', 'light/README.md');
/* PUBLISH */ /* PUBLISH */

View File

@@ -70,10 +70,6 @@ var startWithMBTiles = function(mbtilesFile) {
} }
var instance = new mbtiles(mbtilesFile, function(err) { var instance = new mbtiles(mbtilesFile, function(err) {
instance.getInfo(function(err, info) { instance.getInfo(function(err, info) {
if (info.format != 'pbf') {
console.log('ERROR: MBTiles format is not "pbf".');
process.exit(1);
}
var bounds = info.bounds; var bounds = info.bounds;
var styleDir = path.resolve(__dirname, "../node_modules/tileserver-gl-styles/"); var styleDir = path.resolve(__dirname, "../node_modules/tileserver-gl-styles/");
@@ -89,15 +85,17 @@ var startWithMBTiles = function(mbtilesFile) {
} }
}, },
"styles": {}, "styles": {},
"data": { "data": {}
"osm2vectortiles": { };
if (info.format == 'pbf' &&
info.name.toLowerCase().indexOf('osm2vectortiles') > -1) {
config['data']['osm2vectortiles'] = {
"mbtiles": path.basename(mbtilesFile) "mbtiles": path.basename(mbtilesFile)
}
}
}; };
var styles = fs.readdirSync(path.resolve(styleDir, 'styles')); var styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
for (var i=0; i < styles.length; i++) { for (var i = 0; i < styles.length; i++) {
var styleFilename = styles[i]; var styleFilename = styles[i];
if (styleFilename.endsWith('.json')) { if (styleFilename.endsWith('.json')) {
var styleObject = { var styleObject = {
@@ -110,6 +108,13 @@ var startWithMBTiles = function(mbtilesFile) {
styleObject; styleObject;
} }
} }
} else {
console.log('WARN: MBTiles not in "osm2vectortiles" format. ' +
'Serving raw data only...');
config['data'][info.id || 'mbtiles'] = {
"mbtiles": path.basename(mbtilesFile)
};
}
if (opts.verbose) { if (opts.verbose) {
console.log(JSON.stringify(config, undefined, 2)); console.log(JSON.stringify(config, undefined, 2));

View File

@@ -24,7 +24,7 @@ var Canvas = require('canvas'),
var utils = require('./utils'); var utils = require('./utils');
var FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)'; var FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+\.?\\d+)';
var SCALE_PATTERN = '@[23]x'; var SCALE_PATTERN = '@[234]x';
var getScale = function(scale) { var getScale = function(scale) {
return (scale || '@1x').slice(1, 2) | 0; return (scale || '@1x').slice(1, 2) | 0;
@@ -202,6 +202,7 @@ module.exports = function(options, repo, params, id, dataResolver) {
// meta url which will be detected when requested // meta url which will be detected when requested
'mbtiles://' + name + '/{z}/{x}/{y}.' + (info.format || 'pbf') 'mbtiles://' + name + '/{z}/{x}/{y}.' + (info.format || 'pbf')
]; ];
delete source.scheme;
if (source.format == 'pbf') { if (source.format == 'pbf') {
map.sources[name].emptyTile = new Buffer(0); map.sources[name].emptyTile = new Buffer(0);
} else { } else {
@@ -239,6 +240,7 @@ module.exports = function(options, repo, params, id, dataResolver) {
map.renderers[1] = createPool(1, 4, 16); map.renderers[1] = createPool(1, 4, 16);
map.renderers[2] = createPool(2, 2, 8); map.renderers[2] = createPool(2, 2, 8);
map.renderers[3] = createPool(3, 2, 4); map.renderers[3] = createPool(3, 2, 4);
map.renderers[4] = createPool(4, 2, 4);
}); });
repo[id] = tileJSON; repo[id] = tileJSON;
@@ -253,7 +255,7 @@ module.exports = function(options, repo, params, id, dataResolver) {
return res.status(400).send('Invalid center'); return res.status(400).send('Invalid center');
} }
if (Math.min(width, height) <= 0 || if (Math.min(width, height) <= 0 ||
Math.max(width, height) * scale > 2048) { Math.max(width, height) * scale > (options.maxSize || 2048)) {
return res.status(400).send('Invalid size'); return res.status(400).send('Invalid size');
} }
if (format == 'png' || format == 'webp') { if (format == 'png' || format == 'webp') {
@@ -316,10 +318,14 @@ module.exports = function(options, repo, params, id, dataResolver) {
} }
if (format == 'png') { if (format == 'png') {
var usePngQuant =
(options.formatQuality || {}).pngQuantization === true;
if (usePngQuant) {
buffer = pngquant.compress(buffer, { buffer = pngquant.compress(buffer, {
quality: [0, formatQuality || 90] quality: [0, formatQuality || 90]
}); });
} }
}
res.set({ res.set({
'Last-Modified': lastModified, 'Last-Modified': lastModified,
@@ -426,14 +432,13 @@ module.exports = function(options, repo, params, id, dataResolver) {
var minCorner = mercator.px([bbox[0], bbox[3]], z), var minCorner = mercator.px([bbox[0], bbox[3]], z),
maxCorner = mercator.px([bbox[2], bbox[1]], z); maxCorner = mercator.px([bbox[2], bbox[1]], z);
while ((((maxCorner[0] - minCorner[0]) * (1 + 2 * padding) > w) || w /= (1 + 2 * padding);
((maxCorner[1] - minCorner[1]) * (1 + 2 * padding) > h)) && z > 0) { h /= (1 + 2 * padding);
z--;
minCorner[0] /= 2; z -= Math.max(
minCorner[1] /= 2; Math.log((maxCorner[0] - minCorner[0]) / w),
maxCorner[0] /= 2; Math.log((maxCorner[1] - minCorner[1]) / h)
maxCorner[1] /= 2; ) / Math.LN2;
}
return z; return z;
}; };
@@ -443,11 +448,12 @@ module.exports = function(options, repo, params, id, dataResolver) {
':scale(' + SCALE_PATTERN + ')?\.:format([\\w]+)'; ':scale(' + SCALE_PATTERN + ')?\.:format([\\w]+)';
var centerPattern = var centerPattern =
util.format(':lon(%s),:lat(%s),:z(\\d+)(@:bearing(%s)(,:pitch(%s))?)?', util.format(':lon(%s),:lat(%s),:z(%s)(@:bearing(%s)(,:pitch(%s))?)?',
FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN); FLOAT_PATTERN, FLOAT_PATTERN, FLOAT_PATTERN,
FLOAT_PATTERN, FLOAT_PATTERN);
app.get(util.format(staticPattern, centerPattern), function(req, res, next) { app.get(util.format(staticPattern, centerPattern), function(req, res, next) {
var z = req.params.z | 0, var z = +req.params.z,
x = +req.params.lon, x = +req.params.lon,
y = +req.params.lat, y = +req.params.lat,
bearing = +(req.params.bearing || '0'), bearing = +(req.params.bearing || '0'),
@@ -457,6 +463,10 @@ module.exports = function(options, repo, params, id, dataResolver) {
scale = getScale(req.params.scale), scale = getScale(req.params.scale),
format = req.params.format; format = req.params.format;
if (z < 0) {
return res.status(404).send('Invalid zoom');
}
var path = extractPathFromQuery(req.query); var path = extractPathFromQuery(req.query);
var overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale, var overlay = renderOverlay(z, x, y, bearing, pitch, w, h, scale,
path, req.query); path, req.query);

View File

@@ -22,7 +22,8 @@ var packageJson = require('../package'),
serve_data = require('./serve_data'), serve_data = require('./serve_data'),
utils = require('./utils'); utils = require('./utils');
if (packageJson.name.slice(-6) !== '-light') { var isLight = packageJson.name.slice(-6) == '-light';
if (!isLight) {
// do not require `serve_rendered` in the light package // do not require `serve_rendered` in the light package
serve_rendered = require('./serve_rendered'); serve_rendered = require('./serve_rendered');
} }
@@ -212,6 +213,7 @@ module.exports = function(opts, callback) {
} }
} }
data['server_version'] = packageJson.name + ' v' + packageJson.version; data['server_version'] = packageJson.name + ' v' + packageJson.version;
data['is_light'] = isLight;
data['key_query_part'] = data['key_query_part'] =
req.query.key ? 'key=' + req.query.key + '&amp;' : ''; req.query.key ? 'key=' + req.query.key + '&amp;' : '';
data['key_query'] = req.query.key ? '?key=' + req.query.key : ''; data['key_query'] = req.query.key ? '?key=' + req.query.key : '';
@@ -247,7 +249,7 @@ module.exports = function(opts, callback) {
var tiles = utils.getTileUrls( var tiles = utils.getTileUrls(
req, style.serving_rendered.tiles, req, style.serving_rendered.tiles,
'styles/' + id, style.serving_rendered.format); 'styles/' + id + '/rendered', style.serving_rendered.format);
style.xyz_link = tiles[0]; style.xyz_link = tiles[0];
} }
}); });

View File

@@ -28,6 +28,8 @@ describe('Static endpoints', function() {
testStatic(prefix, '0,0,0/300x300', 'png', 200, 2); testStatic(prefix, '0,0,0/300x300', 'png', 200, 2);
testStatic(prefix, '0,0,0/300x300', 'png', 200, 3); testStatic(prefix, '0,0,0/300x300', 'png', 200, 3);
testStatic(prefix, '0,0,1.5/256x256', 'png', 200);
testStatic(prefix, '80,40,20/600x300', 'png', 200, 3); testStatic(prefix, '80,40,20/600x300', 'png', 200, 3);
testStatic(prefix, '8.5,40.5,20/300x150', 'png', 200, 3); testStatic(prefix, '8.5,40.5,20/300x150', 'png', 200, 3);
testStatic(prefix, '-8.5,-40.5,20/300x150', 'png', 200, 3); testStatic(prefix, '-8.5,-40.5,20/300x150', 'png', 200, 3);
@@ -48,7 +50,6 @@ describe('Static endpoints', function() {
testStatic(prefix, '0,0,0/256x256', 'png', 404, 1); testStatic(prefix, '0,0,0/256x256', 'png', 404, 1);
testStatic(prefix, '0,0,-1/256x256', 'png', 404); testStatic(prefix, '0,0,-1/256x256', 'png', 404);
testStatic(prefix, '0,0,1.5/256x256', 'png', 404);
testStatic(prefix, '0,0,0/256.5x256.5', 'png', 404); testStatic(prefix, '0,0,0/256.5x256.5', 'png', 404);
testStatic(prefix, '0,0,0,/256x256', 'png', 404); testStatic(prefix, '0,0,0,/256x256', 'png', 404);

View File

@@ -26,6 +26,7 @@ describe('Raster tiles', function() {
testTile(prefix, 0, 0, 0, 'png', 200, 2); testTile(prefix, 0, 0, 0, 'png', 200, 2);
testTile(prefix, 0, 0, 0, 'png', 200, 3); testTile(prefix, 0, 0, 0, 'png', 200, 3);
testTile(prefix, 2, 1, 1, 'png', 200, 3); testTile(prefix, 2, 1, 1, 'png', 200, 3);
testTile(prefix, 0, 0, 0, 'png', 200, 4);
}); });
}); });
@@ -39,7 +40,7 @@ describe('Raster tiles', function() {
testTile(prefix, 0, 0, 0, 'pbf', 400); testTile(prefix, 0, 0, 0, 'pbf', 400);
testTile(prefix, 0, 0, 0, 'png', 404, 1); testTile(prefix, 0, 0, 0, 'png', 404, 1);
testTile(prefix, 0, 0, 0, 'png', 404, 4); testTile(prefix, 0, 0, 0, 'png', 404, 5);
//testTile('hybrid', 0, 0, 0, 'png', 404); //TODO: test this //testTile('hybrid', 0, 0, 0, 'png', 404); //TODO: test this
}); });