├── .gitignore ├── README.md ├── app.js ├── public ├── google_point_8.png ├── index.html └── map.js ├── run.sh └── server ├── app.js ├── settings.js └── styles ├── line.xml ├── point.png ├── point.xml ├── point_fast.xml ├── polygon.xml └── text.xml /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .notes.txt 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coalition 2 | 3 | An illustrative web server that combines nodejs, mapnik, and postgis. 4 | 5 | 6 | ## Depends 7 | 8 | NodeJS > 0.2.4 9 | 10 | Mapnik (latest trunk >r2397) 11 | 12 | Node-mapnik 13 | 14 | Npm / Spark 15 | 16 | 17 | ## Installation 18 | 19 | Install node: 20 | 21 | $ wget http://nodejs.org/dist/node-v0.4.0.tar.gz 22 | $ tar xvf node-v0.4.0.tar.gz 23 | $ ./configure 24 | $ make 25 | $ make install 26 | 27 | Install node-mapnik: 28 | 29 | $ git clone git://github.com/mapnik/node-mapnik.git 30 | $ cd node-mapnik 31 | $ ./configure 32 | $ make 33 | $ make install 34 | 35 | Install npm (node package manager) 36 | 37 | $ curl http://npmjs.org/install.sh | sh 38 | 39 | Install spark via npm: 40 | 41 | $ npm install spark 42 | 43 | 44 | ## Configuration 45 | 46 | Edit the postgis settings in 'server/settings.js' to match your system. 47 | 48 | Also, fixup the few hardcoded sample queries in 'public/index.html' to match your postgis tables. 49 | 50 | 51 | ## Usage 52 | 53 | 54 | Start the server by typing: 55 | 56 | $ ./run.sh 57 | 58 | Or: 59 | 60 | $ spark 61 | 62 | Then visit http://localhost:3000/. Choose a style type and a postgis subquery. 63 | 64 | 65 | ## A note on styles 66 | 67 | Mapnik supports applying arbitrary styles to layers, but for the results 68 | to be reasonable you have to apply reasonable styles. For example a PointSymbolizer 69 | will work against either point geometries or polygon geometries, but a PolygonSymbolizer 70 | will not render anything if applied to point geometries. 71 | 72 | 73 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./server/app').server; 2 | -------------------------------------------------------------------------------- /public/google_point_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmeyer/coalition/c4bdcf228b8732382e80f90e160f4aec1b3b5e30/public/google_point_8.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 26 | 27 | 28 |
29 | 30 |
31 |

Demo of sending arbitrary SQL and Styles to Mapnik for dynamic tile generation

32 |
33 | 34 |
35 | Style: 36 |
37 | Point
38 | Polygon
39 | Line
40 |
41 |
42 | 43 |
44 | 45 | 46 | Sample Queries: points9 | polygons9 47 |
48 | (Select * from points9 where marine = 't') as t 49 |
50 | (Select * from polygons9 where marine = 'f') as t 51 |
52 |
53 | 54 |
55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /public/map.js: -------------------------------------------------------------------------------- 1 | var po = org.polymaps; 2 | 3 | var map = po.map() 4 | .container(document.getElementById('map').appendChild(po.svg('svg'))) 5 | .center({lat: 0, lon: 0}) 6 | .zoom(1) 7 | .zoomRange([1, 20]) 8 | .add(po.drag()) 9 | .add(po.wheel()) 10 | .add(po.dblclick()) 11 | .add(po.hash()); 12 | 13 | // mapquest's mapnik tiles 14 | 15 | map.add(po.image() 16 | .url(po.url('http://otile{S}.mqcdn.com/tiles/1.0.0/osm/' 17 | + '{Z}/{X}/{Y}.png') 18 | .hosts(['1', '2', '3', '4']))); 19 | 20 | 21 | var tile_url = '?x={X}&y={Y}&z={Z}'; 22 | 23 | // local tile server 24 | var layer = po.image() 25 | .url(po.url(tile_url + '&sql=points9&style=point')); 26 | 27 | map.add(layer); 28 | 29 | 30 | var setVal = function(value) 31 | { 32 | document.getElementById('q').value = value; 33 | update_tiles(); 34 | } 35 | 36 | var update_tiles = function() { 37 | var style = 'point'; 38 | var sql = document.getElementById('q').value; 39 | if (!sql) 40 | sql = 'points9'; 41 | if (document.forms[0][0].checked) 42 | style = 'point'; 43 | if (document.forms[0][1].checked) 44 | style = 'polygon'; 45 | if (document.forms[0][2].checked) 46 | style = 'line'; 47 | layer.url(po.url(tile_url + '&sql=' + escape(sql) + '&style=' + style)) 48 | .reload(); 49 | console.log(sql); 50 | }; 51 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://github.com/senchalabs/spark 4 | spark -p 3000 -n `sysctl hw.ncpu | awk '{print $2}'` -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | var mapnik = require('mapnik') 2 | , mercator = require('mapnik/sphericalmercator') 3 | , http = require('http') 4 | , url = require('url') 5 | , fs = require('fs') 6 | , path = require('path') 7 | , settings = require('./settings'); 8 | 9 | function isEmpty(obj) { 10 | for(var prop in obj) { 11 | if(obj.hasOwnProperty(prop)) 12 | return false; 13 | } 14 | return true; 15 | } 16 | 17 | module.exports.server = http.createServer(function(req, res) { 18 | 19 | var query = url.parse(req.url.toLowerCase(), true).query; 20 | 21 | res.writeHead(500, { 22 | 'Content-Type': 'text/plain' 23 | }); 24 | 25 | if (!query || isEmpty(query)) { 26 | try { 27 | res.writeHead(200, { 28 | 'Content-Type': 'text/html' 29 | }); 30 | if (req.url == '/') { 31 | res.end(fs.readFileSync('./public/index.html')); 32 | } else { 33 | res.end(fs.readFileSync('./public/' + req.url)); 34 | } 35 | } catch (err) { 36 | res.end('Not found: ' + req.url); 37 | } 38 | } else { 39 | 40 | if (query && 41 | query.x !== undefined && 42 | query.y !== undefined && 43 | query.z !== undefined && 44 | query.sql !== undefined && 45 | query.style !== undefined 46 | ) { 47 | 48 | var bbox = mercator.xyz_to_envelope(parseInt(query.x), 49 | parseInt(query.y), 50 | parseInt(query.z), false); 51 | var map = new mapnik.Map(256, 256, mercator.srs); 52 | map.buffer_size(50); 53 | var layer = new mapnik.Layer('tile', mercator.srs); 54 | try { 55 | settings.postgis.table = unescape(query.sql); 56 | var postgis = new mapnik.Datasource(settings.postgis); 57 | layer.datasource = postgis; 58 | styles = [query.style]; 59 | map.load(path.join(settings.styles, query.style + '.xml')); 60 | // labels 61 | styles.push('text'); 62 | map.load(path.join(settings.styles, 'text.xml')); 63 | layer.styles = styles; 64 | map.add_layer(layer); 65 | // show map in terminal with toString() 66 | //console.log(map.toString()); 67 | } 68 | catch (err) { 69 | res.end(err.message); 70 | } 71 | 72 | map.render(bbox, 'png', function(err, buffer) { 73 | if (err) { 74 | res.end(err.message); 75 | } else { 76 | //console.log(map.scaleDenominator()); 77 | res.writeHead(200, { 78 | 'Content-Type': 'image/png' 79 | }); 80 | res.end(buffer); 81 | } 82 | }); 83 | } else { 84 | res.end('missing x, y, z, sql, or style parameter'); 85 | } 86 | } 87 | }); 88 | -------------------------------------------------------------------------------- /server/settings.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | 3 | module.exports.styles = path.join(__dirname,'styles'); 4 | 5 | module.exports.postgis = { 6 | 'dbname' : 'tiledb', 7 | 'extent' : '-20005048.4188,-9039211.13765,19907487.2779,17096598.5401', 8 | 'geometry_field' : 'the_geom', 9 | 'srid' : 900913, 10 | 'user' : 'postgres', 11 | 'max_size' : 1, 12 | 'type' : 'postgis' 13 | }; 14 | 15 | 16 | -------------------------------------------------------------------------------- /server/styles/line.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | -------------------------------------------------------------------------------- /server/styles/point.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/springmeyer/coalition/c4bdcf228b8732382e80f90e160f4aec1b3b5e30/server/styles/point.png -------------------------------------------------------------------------------- /server/styles/point.xml: -------------------------------------------------------------------------------- 1 | 2 | 250000000000"> 4 | 500000000"> 5 | 200000000"> 6 | 200000000"> 7 | 100000000"> 8 | 100000000"> 9 | 50000000"> 10 | 50000000"> 11 | 25000000"> 12 | 25000000"> 13 | 12500000"> 14 | 12500000"> 15 | 6500000"> 16 | 6500000"> 17 | 3000000"> 18 | 3000000"> 19 | 1500000"> 20 | 1500000"> 21 | 750000"> 22 | 750000"> 23 | 400000"> 24 | 400000"> 25 | 200000"> 26 | 200000"> 27 | 100000"> 28 | 100000"> 29 | 50000"> 30 | 50000"> 31 | 25000"> 32 | 25000"> 33 | 12500"> 34 | 12500"> 35 | 5000"> 36 | 5000"> 37 | 2500"> 38 | 2500"> 39 | 1000"> 40 | 1"> 41 | ]> 42 | 43 | 78 | -------------------------------------------------------------------------------- /server/styles/point_fast.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | -------------------------------------------------------------------------------- /server/styles/polygon.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /server/styles/text.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | --------------------------------------------------------------------------------