Skip to content

Commit

Permalink
Improve tool
Browse files Browse the repository at this point in the history
Fix make tile script path

Update README.md

Update README.md

Update README.md

Fix make-tile index issue

tidy
  • Loading branch information
wkh237 committed Dec 17, 2016
1 parent e0de097 commit 7ca5d8f
Show file tree
Hide file tree
Showing 6 changed files with 462 additions and 243 deletions.
96 changes: 83 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,101 @@
# TMS server
# Make Custom Tiles Pack

This is an instruction about making custom map tiles using [node-map-tiles-editor](https://github.com/wkh237/node-map-tiles-editor).

## Prerequisites

- Node.JS 7.1.0 +
- [node-canvas related dependencies](https://github.com/Automattic/node-canvas#installation) installed
- Node.JS > 7.1.
- **node-canvas** platform dependencies installed. [see instruction](https://github.com/Automattic/node-canvas#installation)

## Prepare Image

`node-map-tiles` simply create tiles by slicing large image into `256x256` tiles, so you will need a single image. In this tutorial, we will use the following image.

![](https://i.imgur.com/FIDasq7.jpg)

It's recommended to use a smaller one for synthesizing the image to map, because the editor is running in browser, it'd be very laggy if the image is huge.

## Start Server

Be sure you have installed everything that `node-canvas` needs, and use Node.JS
`7.1.0+`.
Simply clone or download the project

```
$ git clone https://github.com/wkh237/node-map-tiles-editor.git
```

Then start the node server

```
$ node .
```

the server will start listen on `localhost:5000`
Now you should be able to visit the editor via browser :

[http://localhost:5000/editor](http://localhost:5000/editor)

## Create a Region

Click the `New` button and input the region's name.

![](https://i.imgur.com/6Y2gaED.png)

You will see a JSON file with the name you just input created at `/regions` folder.


## Find a Rough Coordinate

Now, we need a rough cooridate for the region. This can be done by the help of Google Map.

![](https://i.imgur.com/mIHvv1n.png)

From this image, the latitude is `22.729250` and longitude is `120.404356`, put them into `Latitude` and `Longitude` input box, and click `Go`.

## Get Tiles
The Map will now centered to the location.

Simply send GET request to this URL :
![](https://i.imgur.com/ZGgdkul.png)

`http://localhost:5000/tiles/region_code/z/x/y`
## Create Bounding Box

where `region_code` should be `test-region1`.
Every region should have a bounding box for slicing tiles. This can be done by simply click on the map and dragging the markers.

![](https://i.imgur.com/qv564h1.png)

After created a bounding box, don't forget to click `Save Changes` button.

## Select Source Image

To change the source image of a region, simply select it from the `Image dropdown`, it will list images inside `/region-raw-img` folder. Will be able to upload directly from browser, but not ATM.

![](https://i.imgur.com/PJLwRgg.png)

You should be able to see the image draw on the map once you selected it.

## Adjust Bounding Box

Now just moving and resizing the bounding box to most appropriate position and click `Save Change`.

![](https://i.imgur.com/MNgJ8Iw.png)

## See Sample Tiles

Now it's time to see the actaul tiles created from server, scroll down to bottom of the page, you should be able to see a list which tells you how many tiles will be generated with different zoom levels.

![](https://i.imgur.com/j6f6eOe.png)


To render sample tiles simply change the arguments on the panel and click `Render Tiles` button.

## Create Tiles

After everything's confirmed, now go to terminal and create tiles via command :

```
# format : node make-tiles <region name> <zoom-min> <zoom-max>
$ node make-tiles example 15 20
```

## Debug Information
![](https://i.imgur.com/fJdFjwA.png)

Send reuqest to the following URL, will generate tiles which has debug information :
You can change source image by modifying `image` property in `/regions/<region name>.json`, the script will find the image with the same name inside `/region-raw-image` folder.

http://localhost:5000/tiles/debug/region_code/z/x/y`
The tiles will located in `/regions/<region name>/output/` folder.
189 changes: 99 additions & 90 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ var imgCache = {};
app.set('port', (process.env.PORT || 5000));

app.use('/public', express.static('public'));
app.use('/editor', express.static('public'));
app.use('/region-raw-img', express.static('region-raw-img'));

app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
Expand All @@ -23,6 +25,60 @@ app.listen(app.get('port'), function() {
console.log('Node app is running on port', app.get('port'));
});

app.get('/', function(req,res) {
res.redirect('/editor/');
})

app.get('/images', function(req, res) {

var images = fs.readdirSync('./region-raw-img/');
res.send(images);

})

app.get('/overlay/:region', function(req, res) {
try {
var region = req.params.region;
var img = JSON.parse(fs.readFileSync('./regions/' + region + '.json')).image;
var overlay = process.cwd() + '/region-raw-img/' + img;
res.sendFile(overlay);
}
catch(err) {
console.log(err.stack);
}
})

app.put('/regions/:name', function(req, res) {

var name = req.params.name;
var body = req.body;
console.log(name, body);
try {
fs.writeFileSync('./regions/'+name+'.json', JSON.stringify(body));
}
catch(err) {
console.log(err.stack);
res.sendStatus(500);
}
res.sendStatus(200);
})

app.post('/regions/:name', function(req, res) {

var name = req.params.name;
var body = req.body;
console.log(name, body);
try {
fs.writeFileSync('./regions/'+name+'.json', JSON.stringify(body));
}
catch(err) {
console.log(err.stack);
res.sendStatus(500);
}
res.sendStatus(200);

})

app.get('/regions', function(req, res) {

try {
Expand Down Expand Up @@ -71,7 +127,7 @@ app.get('/bounds/:region', function(req, res) {

})

app.get('/reset-cache/:region', function(req,res) {
app.delete('/tiles/:region', function(req,res) {

var region = req.params.region;
try {
Expand Down Expand Up @@ -103,12 +159,6 @@ app.get('/lookup/:z/:x/:y', function(req, res) {

});

app.get('/dummy-tiles/:region/:z/:x/:y', function (req, res) {
var {x, y, z, region} = req.params;
console.log('dummy tile: ', region, x, y, z);
res.type('image/png').send(generateTile(x,y,z));
});

app.get('/tiles/debug/:region/:z/:x/:y/', handleTileRequestDebug);
app.get('/tiles/:region/:z/:x/:y/', handleTileRequest);

Expand All @@ -118,24 +168,28 @@ function handleTileRequestDebug(req, res) {
}

function handleTileRequest(req, res) {
var {x, y, z, region, debug} = req.params;
console.log(`make tile: ${region}/${z}/${x}/${y}`);
let tilePath = `./regions/${region}/${debug ? 'debug/' : ''}${z}/${x}${y}.png`;
fs.exists(tilePath, (ext) => {

if(ext) {
console.log('found tile', tilePath);
fs.readFile(tilePath, (err, data) => {
res.type('image/png').send(data);
});
}
else {
createTileFromRawImage(region, x, y, z, debug, function(bytes) {
console.log('send tile')
res.type('image/png').send(bytes);
});
}
});
try {
var {x, y, z, region, debug} = req.params;
console.log(`make tile: ${region}/${z}/${x}/${y}`);
let tilePath = `./regions/${region}/${debug ? 'debug/' : ''}${z}/${x}${y}.png`;
fs.exists(tilePath, (ext) => {

if(ext) {
console.log('found tile', tilePath);
fs.readFile(tilePath, (err, data) => {
res.type('image/png').send(data);
});
}
else {
createTileFromRawImage(region, x, y, z, debug, function(bytes) {
console.log('send tile')
res.type('image/png').send(bytes);
});
}
});
} catch(err) {
console.log(err.stack)
}
}

app.get('/base/:z/:x/:y', function(req,res) {
Expand All @@ -156,45 +210,6 @@ app.get('/base/:z/:x/:y', function(req,res) {
});
})

app.get('/exps/:id', function(req, res) {
res.send(dummyExperience(req.params.id));
});

app.get('/pins', function(req, res) {
res.send(regions.pins);
});

app.get('/pins/:id', function(req, res) {
res.send(pins[req.params.id]);
});

function dummyExperience(id) {
var type = Math.random() > 0.8 ? 'label' : 'link';
if(type === 'link') {
var expired = Math.random() > 0.6;
var image = Math.floor((Math.random() - 0.01) * 4);

return {
exp_id : 'Experience #' + id,
label : 'Experience #' + id,
state : expired ? 'expired' : 'current',
start : expired ? '2016/11/08 14:30:00' : moment(Date.now() - 3600000 * Math.random()*2).format('YYYY/MM/DD HH:mm:ss'),
end : expired ? '2016/11/08 15:00:00' : moment(Date.now() + 3600000 * Math.random()*5).format('YYYY/MM/DD HH:mm:ss'),
hero_photo : 'public/event-bg' + image + '.png',
content : 'this is the text description',
pin_id : null
};
}
else {
return {
exp_id : 'Experience #' + id,
label : 'Label only',
content : 'this is the text description',
pin_id : null
};
}
};

function tileToLatLng(x,y,z) {
let result = { lat : tileToLat(y,z), lng : tileToLong(x,z) };;
return result;
Expand All @@ -213,9 +228,19 @@ function createTileFromRawImage(region, x, y, z, debug, cb) {
x = Math.floor(x);
y = Math.floor(y);
z = Math.floor(z);
let bounds = fs.readFile(`./regions/${region}.json`, (err, data) => {

let {bounds} = JSON.parse(data);
fs.readFile(`./regions/${region}.json`, (err, data) => {
let bounds = [];
let img = '';
try {
bounds = JSON.parse(data).bounds;
img = JSON.parse(data).image;
console.log(bounds);
}
catch(err) {
console.log(err.stack);
return;
}
let sign = bounds[0].lat < 0 ? 1 : -1;
let tileBounds = [
tileToLatLng(x,y,z),
tileToLatLng(x+1,y,z),
Expand All @@ -226,7 +251,7 @@ function createTileFromRawImage(region, x, y, z, debug, cb) {
render(null, imgCache);
}
else {
fs.readFile(`./region-raw-img/${region}.png`, render);
fs.readFile(`./region-raw-img/${img}`, render);
}

function render(err, data) {
Expand All @@ -248,6 +273,9 @@ function createTileFromRawImage(region, x, y, z, debug, cb) {
let tileImg = new Canvas(256, 256);
let originX = (Math.abs(tileBounds[0].lng) - Math.abs(bounds[0].lng))/regionWidth * img.width;
let originY = (Math.abs(tileBounds[0].lat) - Math.abs(bounds[0].lat))/regionHeight * img.height;
if(sign<0) {
originY = img.height - originY - 512 - img.height%256;
}
let ctx = tileImg.getContext('2d');

if( originX > img.width + tileWidth ||
Expand All @@ -268,17 +296,18 @@ function createTileFromRawImage(region, x, y, z, debug, cb) {

console.log(`fill size (${tileWidth}, ${tileHeight})`);
console.log(`project (${originX}, ${originY}, ${originX + tileWidth}, ${originY + tileHeight})`);

ctx.drawImage(img, originX, originY, tileWidth, tileHeight, 0, 0, 256, 256);
if(debug) {
drawText(ctx,{region, x,y,z}, originX,originY,tileWidth, tileHeight, z);
drawText(ctx,{x,y,z}, originX,originY,tileWidth, tileHeight, z);
}
ctx.strokeRect(0, 0, 256, 256);
ctx.fillStyle = '#DDD';
}

var bytes = tileImg.toBuffer(undefined, 3, ctx.PNG_FILTER_NONE);
console.log('size', bytes.length);
var dir = `./regions/${region}/${debug ? 'debug/' : ''}${z}/${x}${y}.png`;
var dir = `./regions/debug/${region}/${z}/${x}${y}.png`;
mkdirp(getDirName(dir), function (err) {
if (err)
console.log(err);
Expand Down Expand Up @@ -316,26 +345,6 @@ function drawText(ctx,param, ox,oy, dx, dy, z) {
ctx.fillText(z + 'x', 144, 255);
}

function generateTile(x,y,z) {

var canvas = new Canvas(256, 256);
var ctx = canvas.getContext('2d');

var coords = '(' + [x, y].join(', ') + ')';
ctx.rect(0, 0, 256, 256);
ctx.fillStyle = '#F0F0F0';
ctx.fill();
ctx.fillStyle = '#333';
ctx.font = '16px Arial';
ctx.fillText(coords, 24, 64);
ctx.strokeStyle = 'white';
ctx.strokeRect(0, 0, 256, 256);
ctx.fillStyle = '#DDD';
ctx.font = '64px Arial';
ctx.fillText(z + 'x', 64, 192);
var bytes = canvas.toBuffer(undefined, 3, canvas.PNG_FILTER_NONE);
return bytes;
}

var deleteFolderRecursive = function(path, cb) {
if( fs.existsSync(path) ) {
Expand Down
Loading

0 comments on commit 7ca5d8f

Please sign in to comment.