Skip to content

Commit 4db469b

Browse files
committed
Split server out from National Map.
0 parents  commit 4db469b

File tree

11 files changed

+548
-0
lines changed

11 files changed

+548
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
# added by GitSavvy
3+
/error.log

LICENSE.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
3+
Copyright(c) 2012-2016 National ICT Australia Limited (NICTA) / Data61.
4+
5+
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
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
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.
10+
11+
Portions of the National Map use the following copyrighted material, the use of which is hereby acknowledged.
12+
13+
Third-Party Code
14+
================
15+
16+
TerriaJS-Server includes the following third-party code.
17+
18+
### Proj4.js
19+
20+
http://proj4js.org/
21+
22+
> Copyright (c) 2014, Mike Adair, Richard Greenwood, Didier Richard, Stephen Irons, Olivier Terral and Calvin Metcalf
23+
> The MIT License (MIT)
24+
>
25+
> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
26+
>
27+
> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
28+
>
29+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30+

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
## TerriaJS-Server
2+
3+
This is a basic NodeJS Express server that serves up a (not included) static TerriaJS site (such as National Map) with a few additional useful services.
4+
5+
### Install
6+
7+
1. Download and build your National Map (or AREMI, etc) somewhere.
8+
2. `git clone https://github.com/terriajs/terriajs-server`
9+
3. `cd terriajs`
10+
4. `ln -s path/to/national-map/wwwroot wwwroot`
11+
5. `npm install`
12+
13+
### Run
14+
15+
1. `npm start`

app.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"use strict";
2+
3+
/**
4+
* Terria server, used to run NationalMap. It is primarily a static web app, but there are a couple of helper functions
5+
* that run server-side.
6+
*/
7+
8+
var cluster = require('cluster');
9+
10+
// The master process just spins up a few workers and quits.
11+
if (cluster.isMaster) {
12+
13+
// Count the machine's CPUs
14+
var cpuCount = require('os').cpus().length;
15+
cpuCount = 1; // TODO REMOVE
16+
console.log('Cores Used:', cpuCount);
17+
18+
// Create a worker for each CPU
19+
for (var i = 0; i < cpuCount; i += 1) {
20+
cluster.fork();
21+
}
22+
23+
// Listen for dying workers
24+
cluster.on('exit', function (worker) {
25+
26+
// Replace the dead worker, we're not sentimental
27+
console.log('Worker ' + worker.id + ' died :(');
28+
cluster.fork();
29+
30+
});
31+
return;
32+
}
33+
34+
/*global console,require,__dirname*/
35+
/*jshint es3:false*/
36+
37+
var express = require('express');
38+
var compression = require('compression');
39+
var path = require('path');
40+
var cors = require('cors');
41+
42+
var proxy = require('./proxy');
43+
var crs = require('./crs');
44+
var convert = require('./convert');
45+
46+
var yargs = require('yargs').options({
47+
'port' : {
48+
'default' : 3001,
49+
'description' : 'Port to listen on.'
50+
},
51+
'public' : {
52+
'type' : 'boolean',
53+
'default' : true,
54+
'description' : 'Run a public server that listens on all interfaces.'
55+
},
56+
'upstream-proxy' : {
57+
'description' : 'A standard proxy server that will be used to retrieve data. Specify a URL including port, e.g. "http://proxy:8000".'
58+
},
59+
'bypass-upstream-proxy-hosts' : {
60+
'description' : 'A comma separated list of hosts that will bypass the specified upstream_proxy, e.g. "lanhost1,lanhost2"'
61+
},
62+
'help' : {
63+
'alias' : 'h',
64+
'type' : 'boolean',
65+
'description' : 'Show this help.'
66+
}
67+
});
68+
var argv = yargs.argv;
69+
70+
if (argv.help) {
71+
return yargs.showHelp();
72+
}
73+
74+
var po = proxy._proxyOptions = {};
75+
po.upstreamProxy = argv['upstream-proxy'];
76+
po.bypassUpstreamProxyHosts = {};
77+
78+
if (argv['bypass-upstream-proxy-hosts']) {
79+
argv['bypass-upstream-proxy-hosts'].split(',').forEach(function(host) {
80+
po.bypassUpstreamProxyHosts[host.toLowerCase()] = true;
81+
});
82+
}
83+
84+
// eventually this mime type configuration will need to change
85+
// https://github.com/visionmedia/send/commit/d2cb54658ce65948b0ed6e5fb5de69d022bef941
86+
var mime = express.static.mime;
87+
mime.define({
88+
'application/json' : ['czml', 'json', 'geojson'],
89+
'text/plain' : ['glsl']
90+
});
91+
92+
// initialise app with standard middlewares
93+
var app = express();
94+
app.use(compression());
95+
app.use(cors());
96+
app.disable('etag');
97+
98+
// Serve the bulk of our application as a static web directory.
99+
app.use(express.static(path.join(__dirname, 'wwwroot')));
100+
101+
app.use('/proxy', proxy); // Proxy for servers that don't support CORS
102+
app.use('/proj4def', crs); // Proj4def lookup service, to avoid downloading all definitions into the client.
103+
app.use('/convert', convert); // OGR2OGR wrapper to allow supporting file types like Shapefile.
104+
app.get('/ping', function(req, res){
105+
res.status(200).send('OK');
106+
});
107+
108+
// Redirect unknown pages back home. We don't actually have a 404 page, for starters.
109+
app.use(function(req, res, next) {
110+
res.redirect(303, '/');
111+
});
112+
113+
app.listen(argv.port, argv.public ? undefined : 'localhost');
114+
115+
/*
116+
//sample simple NM service. To use, uncomment and move above the fallback redirection.
117+
app.post('/nm_service_1', function(req, res, next) {
118+
var formidable = require('formidable');
119+
//receive the posted object
120+
var form = new formidable.IncomingForm();
121+
form.parse(req, function(err, fields, files) {
122+
//create a layer for NM to display
123+
var obj = {
124+
name: 'Bikes Available',
125+
type: 'DATA',
126+
proxy: false,
127+
url: 'http://nationalmap.nicta.com.au/test/bike_racks.geojson'
128+
};
129+
//send a response with the object and display text
130+
res.json({ displayHtml: 'Here are the available bike racks.', layer: obj});
131+
});
132+
});
133+
*/

config.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"proxyDomains" : [
3+
"nicta.com.au",
4+
"gov.au",
5+
"csiro.au",
6+
"arcgis.com",
7+
"argo.jcommops.org",
8+
"www.abc.net.au",
9+
"geoserver.aurin.org.au",
10+
"mapsengine.google.com",
11+
"s3-ap-southeast-2.amazonaws.com",
12+
"adelaidecitycouncil.com",
13+
"www.dptiapps.com.au",
14+
"geoserver-123.aodn.org.au",
15+
"geoserver.imos.org.au",
16+
"nci.org.au"
17+
]
18+
}
19+

convert.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"use strict";
2+
var express = require('express');
3+
var router = express.Router();
4+
5+
var fs = require('fs');
6+
var ogr2ogr = require('ogr2ogr');
7+
var request = require('request');
8+
var formidable = require('formidable');
9+
10+
// provide conversion to geojson service
11+
// reguires install of gdal on server: sudo apt-get install gdal-bin
12+
router.post('/', function(req, res, next) {
13+
var form = new formidable.IncomingForm();
14+
form.parse(req, function(err, fields, files) {
15+
var fname, fpath, inputStream;
16+
var maxSize = 1000000;
17+
18+
if (fields.input_url !== undefined) {
19+
if (fields.input_url.indexOf('http') === 0) {
20+
fpath = fields.input_url;
21+
fname = fpath;
22+
}
23+
} else if (files.input_file !== undefined) {
24+
if (files.input_file.size <= maxSize) {
25+
fpath = files.input_file.path;
26+
fname = files.input_file.name;
27+
} else {
28+
console.log('Input file is too large', files.input_file.size);
29+
}
30+
}
31+
if (fpath === undefined) {
32+
res.status(500).send('Unable to convert data');
33+
return;
34+
}
35+
console.log('Converting', fname);
36+
37+
var hint = '';
38+
//simple hint for now, might need to crack zip files going forward
39+
if (fname.toLowerCase().indexOf('.zip') === fname.length-4) {
40+
hint = 'shp';
41+
}
42+
43+
if (fpath.indexOf('http') === 0) {
44+
inputStream = request.get({url: fpath}).on('response', function(response) {
45+
var request = this, len = 0;
46+
response.on('data', function (chunk) {
47+
len += chunk.length;
48+
if (len > maxSize) {
49+
request.abort();
50+
}
51+
});
52+
response.on('end', function() {
53+
console.log('Convert download size', len);
54+
});
55+
});
56+
} else {
57+
inputStream = fs.createReadStream(fpath);
58+
}
59+
60+
var ogr = ogr2ogr(inputStream, hint)
61+
.skipfailures()
62+
.options(['-t_srs', 'EPSG:4326']);
63+
64+
ogr.exec(function (er, data) {
65+
if (er) {
66+
console.error(er);
67+
}
68+
if (data !== undefined) {
69+
res.status(200).send(JSON.stringify(data));
70+
} else {
71+
res.status(500).send('Unable to convert data');
72+
}
73+
});
74+
});
75+
});
76+
77+
module.exports = router;

crs.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"use strict";
2+
var express = require('express');
3+
var router = express.Router();
4+
5+
var proj4 = require('proj4');
6+
7+
//TODO: check if this loads the file into each core and if so then,
8+
require('proj4js-defs/epsg')(proj4);
9+
10+
11+
//provide REST service for proj4 definition strings
12+
router.get('/:crs', function(req, res, next) {
13+
var crs = req.param('crs');
14+
var epsg = proj4.defs[crs.toUpperCase()];
15+
if (epsg !== undefined) {
16+
res.status(200).send(epsg);
17+
} else {
18+
res.status(404).send('No proj4 definition available for this CRS.');
19+
}
20+
});
21+
22+
module.exports = router;

package.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"name": "terria-server",
3+
"version": "0.0.1",
4+
"description": "NodeJS server for TerriaJS, consisting of a CORS proxy, proj4 CRS lookup service, ogr2ogr conversion service, and express static server.",
5+
"main": "app.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"start": "node_modules/.bin/supervisor app"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "https://github.com/TerriaJS/TerriaJS-Server"
13+
},
14+
"keywords": [
15+
"terriajs",
16+
"National",
17+
"Map"
18+
],
19+
"license": "Apache 2.0",
20+
"bugs": {
21+
"url": "https://github.com/TerriaJS/TerriaJS-Server/issues"
22+
},
23+
"homepage": "https://github.com/TerriaJS/TerriaJS-Server",
24+
"dependencies": {
25+
"compression": "^1.6.0",
26+
"cors": "^2.7.1",
27+
"express": "^4.13.3",
28+
"formidable": "^1.0.17",
29+
"ogr2ogr": "^0.5.1",
30+
"proj4": "^2.3.12",
31+
"proj4js-defs": "0.0.1",
32+
"request": "^2.67.0",
33+
"supervisor": "^0.9.1",
34+
"yargs": "^3.31.0"
35+
}
36+
}

0 commit comments

Comments
 (0)