This will be a series of posts on leveraging Node.js to generate tile-images on server-side that will be displayed on top of Bing Maps (although applicable to any other mapping API).
For those not familiar with it, Node has gained lots of momentum on the web. It provides a simple event oriented, non-blocking architecture on which one can develop fast and scalable applications using Javascript.
It lends itself really well for generating tile-images and is in-fact being used by some large players like Mapbox.
It lends itself really well for generating tile-images and is in-fact being used by some large players like Mapbox.
I'm going to use an amazing Node module called Canvas which provides the whole HTML5 Canvas API on server-side. My main reason is that it's an API that I know particularly well and I can reuse lots of my existing code.
I'm going to develop on Mac OS X but most of this would be exactly the same on Windows or Linux.
Afterwards, as an extra, I'm also going to provide instructions on how to setup a Linux Virtual Machine in Azure running the resulting node application.
I'm going to develop on Mac OS X but most of this would be exactly the same on Windows or Linux.
Afterwards, as an extra, I'm also going to provide instructions on how to setup a Linux Virtual Machine in Azure running the resulting node application.
First things first. I'll setup an bare-bones node application and gradually add more stuff to it.
Install Node and create a file named server.js with the following content:
console.log("Hello World");Run this node application in a terminal window:
node server.js
Not particularly impressive but we now know that Node is working. To return an image I'll use, as mentioned, the Canvas module. Installing the module is simple enough (npm install canvas), but the problem is that it has a dependency on a lib called Cairo. Installing Cairo requires one to follow a couple of installation steps religiously (includes information for Windows, Linux, Mac OS X, et al.)
https://github.com/LearnBoost/node-canvas/wiki/_pages
After installing Cairo and the Canvas module we're ready to start. Let's just generate a simple image on the fly and return it as a server response.
Update the node server to return an image generated on the fly with Canvas:
var http = require('http'); var Canvas = require('canvas'); var server = http.createServer(function (request, response) { var canvas = new Canvas(256, 256) var context = canvas.getContext('2d'); context.beginPath(); context.rect(0, 0, 256, 256); context.lineWidth = 7; context.strokeStyle = 'black'; context.stroke(); var stream = canvas.createPNGStream(); response.writeHead(200, {"Content-Type": "image/png"}); stream.pipe(response); }); server.listen(8000);After running this server and pointing the browser to localhost:8000 a simple 256x256 image should be displayed.
In Node, to create an HTTP Server, a module named Express is invaluable. It provides loads of functionality including a routing engine that I'll use in this demo to obtain the tile's zxy and display it inside the square. You'll have to install it using npm.
npm install expressUpdate the server to parse the request and paint a text with the Z, X, Y
var express = require('express'); var Canvas = require('canvas'); var app = express(); app.get('/:z/:x/:y', function(req, res) { var z = req.params.z, x = req.params.x, y = req.params.y; var canvas = new Canvas(256, 256) var context = canvas.getContext('2d'); context.beginPath(); context.rect(0, 0, 256, 256); context.lineWidth = 7; context.strokeStyle = 'black'; context.stroke(); context.fillStyle = "darkblue"; context.font = "bold 16px Arial"; context.fillText(z + "/" + x + "/" + y, 100, 128); var stream = canvas.createPNGStream(); res.type("png"); stream.pipe(res); }); app.listen(process.env.PORT || 8000);
When running the server it will use the url parameters as http://localhost:8000/z/x/y and display them inside the square:
The last step will be creating an Html page with a Bing Map that uses these tiles.
index.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Bing Maps Server Tile Layer - Simple Demo</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body onload="loadMap();"> <div id='mapDiv' style="width:100%; height: 100%;"></div> <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script> <script type="text/javascript"> function loadMap() { var MM = Microsoft.Maps; var map = new MM.Map(document.getElementById("mapDiv"), { center: new MM.Location(39.5, -8), mapTypeId: MM.MapTypeId.road, zoom: 7, credentials:"YOUR CREDENTIALS HERE"}); var tileSource = new MM.TileSource({ uriConstructor: function (tile) { var x = tile.x; var z = tile.levelOfDetail; var y = tile.y; return "http://localhost:8000/" + z + "/" + x + "/" + y; }}); var tileLayer = new MM.TileLayer({ mercator: tileSource}); map.entities.push(tileLayer); } </script> </body> </html>A map should appear with an additional layer with the generated tiles.
You can check the end-result here.
Extra:
I've deployed the above example in a Linux Virtual Machine on Azure. I'm going to show you the exact steps that I took to do so.
On the Azure Portal press "Add":
Choose "Virtual Machine"
Choose Ubuntu Server 12.04 LTS from the UBUNTU section.
Fill the details. The Virtual Machine Name will have to be something unique. You'll be assigned an URL like <virtual machine name>.cloudapp.net.
Instead of using a SSH Key I would suggest, for simplicity, to just set a user name and a password.
The wizard then asks which ports you want open. The SSH appears by default and is mandatory to be able to manage the machine. I've also configured two HTTP endpoints and one for FTP.
After finishing the process it should take a couple of minutes to finalise the Virtual Machine.
Afterwards use a SSH client to access the machine (putty on windows or ssh on Mac OS X):ssh <virtual machine name>.cloudapp.net -l <user>For my particular example:
ssh build-failed.cloudapp.net -l pedro
The first thing to do inside the machine would be to install Node.
Just run the following commands as recommended on http://kb.solarvps.com/ubuntu/installing-node-js-on-ubuntu-12-04-lts/.
root@nodepod:~# sudo apt-get update root@nodepod:~# sudo apt-get install python-software-properties python g++ make root@nodepod:~# sudo add-apt-repository ppa:chris-lea/node.js root@nodepod:~# sudo apt-get update root@nodepod:~# sudo apt-get install nodejsI also had to install Cairo according to the instructions at: https://github.com/LearnBoost/node-canvas/wiki/Installation---Ubuntu
sudo apt-get update sudo apt-get install libcairo2-dev libjpeg8-dev libpango1.0-dev libgif-dev build-essential g++Now, to copy the files I've developed previously on this post I'm going to setup an FTP server on the Virtual Machine. I've used the page at https://help.ubuntu.com/12.04/serverguide/ftp-server.html as reference:
sudo apt-get install vsftpdI will have the FTP configured as:
- Each local user will have access to the FTP
- Anonymous access is not allowed
- Write permission is allowed
anonymous_enable=NO local_enable=YES write_enable=YESNow just restart the FTP server and you should be set to go:
sudo restart vsftpdNow just copy the files over to the server and execute the app with a "&" for it to run in the background.
node server.js &I've actually updated the server.js code slightly so that it includes:
- compression
- cache headers
- a route to serve the index.html file.
The complete server.js should be:
var express = require('express'); var Canvas = require('canvas'); var app = express(); app.use(express.compress()); app.get('/', function(req,res) { res.sendfile('public/index.html'); }); app.get('/:z/:x/:y', function(req, res) { res.setHeader("Cache-Control", "max-age=31556926"); var z = req.params.z, x = req.params.x, y = req.params.y; var canvas = new Canvas(256, 256) var context = canvas.getContext('2d'); context.beginPath(); context.rect(0, 0, 256, 256); context.lineWidth = 7; context.strokeStyle = 'black'; context.stroke(); context.fillStyle = "darkblue"; context.font = "bold 16px Arial"; context.fillText(z + "/" + x + "/" + y, 100, 128); var stream = canvas.createPNGStream(); res.type("png"); stream.pipe(res); }); app.listen(process.env.PORT || 8000);
Muito bom ... obrigado pela partilha
ReplyDeleteSempre às ordens :)
DeletePedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download Now
Delete>>>>> Download Full
Pedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download LINK
>>>>> Download Now
Pedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download Full
>>>>> Download LINK 6x
This is awesome,
ReplyDeleteThank you so much for this tutorial,
:)
This comment has been removed by the author.
ReplyDeletewow, i'm looking for this tutorial, you explained very well..
ReplyDeletei try this tutorial and works 100% thanks
This is very nice post. Thanks for sharing this post with us
ReplyDeleteNode JS Online training
Node JS training in Hyderabad
The blog here you provided is a valuable content for us. This is one amazing piece of article. Helped a lot in increasing my knowledge
ReplyDeleteNode JS Online training
Node JS training in Hyderabad
Pedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download Now
ReplyDelete>>>>> Download Full
Pedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download LINK
>>>>> Download Now
Pedro'S Tech Mumblings: Generating Server-Side Tile-Maps With Node.Js (Part 1 - Programatically With Canvas) >>>>> Download Full
>>>>> Download LINK 63