Skip to content

Commit

Permalink
Merge pull request #5 from Turistforeningen/v2-rewrite
Browse files Browse the repository at this point in the history
Raido API v2 rewrite
  • Loading branch information
Starefossen authored Jun 17, 2016
2 parents ebc90a7 + 7251257 commit e88dc90
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 107 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:argon-slim
FROM node:6-slim

# Add our user and group first to make sure their IDs get assigned consistently
RUN groupadd -r app && useradd -r -g app app
Expand Down
49 changes: 37 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ Anglo-Saxon Rad.

### GET /v1/routing

* **string** `cords` - A to B coordinates on the format `x1,y1,x2,y2`
* **number** `sensitivity` - routing sensitivity / buffer (**default** `2000`)
* **string** `source` - start point coordinate on the format `x,y`
* **string** `target` - end point coordinate on the format `x,y`
* **number** `path_buffer` - route sensitivity / buffer (**default** `2000`)
* **number** `point_buffer` - point sensitivity / buffer (**default** `10`)
* **string** `bbox` - bbox bounding bounds on the format `x1,y1,x2,y2`
* **number** `limit` - max number of shortest path to return (**default** `1`)

Return the shortest path from coordinate A to coordinate B. Will return a
`GeometryCollection` if a route is found.
Return shortest path from `source` to `target`. Returns a `GeometryCollection`
if a route is found.

**Return**
**Returned route**

```json
{
Expand All @@ -46,18 +50,39 @@ Return the shortest path from coordinate A to coordinate B. Will return a
}
```

**Mutliple routes**

If you want multiple shortest path you can use the `limit` query parameter to
control the number of routes returned. By default only the shortest route will
be returned.

```json
{
"type": "GeometryCollection",
"geometries": [{
"type": "LineString",
"coordinates": [...],
"properties": {
cost: 1510.05825002283
}
},{
"type": "LineString",
"coordinates": [...],
"properties": {
cost: 1610.06825002284
}
}]
}

**Route not found**

If the point A or B can not be found or a route between them could not be
found the routing will return a `LineString` between the two points.
If the `source` or `target` points can not be found or a route between them
could not be found the routing will return an empty `GeometryCollection`.

```json
{
"type": "LineString",
"coordinates": [
[ 8.922786712646484, 61.5062387475475 ],
[ 8.97857666015625, 61.50984184413987 ]
]
"type": "GeometryCollection",
"geometries": []
}
```

Expand Down
77 changes: 55 additions & 22 deletions controllers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,77 @@ const app = express();
const HttpError = require('@starefossen/http-error');

app.get('/routing', (req, res, next) => {
if (!req.query.coords || req.query.coords.split(',').length !== 4) {
return next(new HttpError('Missing or invalid "coords" query', 400));
// Convert all coordinates to propper float values
const source = (req.query.source || '')
.split(',')
.map(coordinate => parseFloat(coordinate, 10))
.filter(coordinate => !isNaN(coordinate));

const target = (req.query.target || '')
.split(',')
.map(coordinate => parseFloat(coordinate, 10))
.filter(coordinate => !isNaN(coordinate));

// Validate required source and target parameters
if (source.length !== 2 || target.length !== 2) {
return next(new HttpError('Missing or invalid coordinates', 400));
}

const bbox = (req.query.bbox || '')
.split(',')
.map(coordinate => parseFloat(coordinate, 10))
.filter(coordinate => !isNaN(coordinate));

// Validate bbox parameter
if (!(bbox.length === 4 || bbox.length === 0)) {
return next(new HttpError('Missing or invalid bbox coordinates', 400));
}

// Make sure all the coords are float values
const coords = req.query.coords.split(',').map(c => parseFloat(c, 10));
const pathBuffer = Math.min(parseInt(req.query.path_buffer || 2000, 10), 4000);
const pointBuffer = Math.min(parseInt(req.query.point_buffer || 10, 10), 1000);
const limit = Math.min(parseInt(req.query.limit || 1, 10), 3);

const sensitivity = Math.min(parseInt(req.query.sensitivity || 2000, 10), 4000);
// Format bbox to propper PostgreSQL array
const bboxPgArr = `{${bbox.join(',')}}`;

const sql = `
SELECT ST_AsGeoJSON(ST_Transform(geom, 4326)) as geojson, cost
FROM path(${coords.join(',')}, ${sensitivity})
LIMIT 1;
const sql = pg.SQL`
SELECT
cost,
ST_AsGeoJSON(ST_Transform(geom, 4326)) as geometry
FROM path(
${source[0]}::double precision,
${source[1]}::double precision,
${target[0]}::double precision,
${target[1]}::double precision,
path_buffer:=${pathBuffer}::integer,
point_buffer:=${pointBuffer}::integer,
bbox:=${bboxPgArr}::double precision[],
targets:=${limit}::integer
)
LIMIT ${limit};
`;

return pg.query(sql, (err, result) => {
if (err) { return next(new HttpError('Database Query Failed', 500, err)); }

if (!result.rows.length || !result.rows[0].geojson) {
return res.json({ type: 'LineString', coordinates: [
[coords[0], coords[1]],
[coords[2], coords[3]],
] });
}

const geojson = {
const collection = {
type: 'GeometryCollection',
geometries: [],
};

result.rows.forEach((row) => {
const geometry = JSON.parse(row.geojson);
geometry.properties = { cost: row.cost };
if (!result.rows.length || !result.rows[0].geometry) {
return res.json(collection);
}

geojson.geometries.push(geometry);
result.rows.forEach((row) => {
collection.geometries.push({
type: 'Feature',
geometry: JSON.parse(row.geometry),
properties: { cost: row.cost },
});
});

return res.json(geojson);
return res.json(collection);
});
});

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: '2'

services:
node:
image: node:argon
image: node:6
network_mode: 'bridge'
working_dir: /usr/src/app
volumes:
Expand Down
7 changes: 7 additions & 0 deletions lib/pg.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,10 @@
const pg = require('pg');

module.exports = new pg.Client('postgres://postgres:@postgres/postgres');

module.exports.SQL = function SQL(parts, ...values) {
return {
text: parts.reduce((prev, curr, i) => `${prev}\$${i}${curr}`),
values,
};
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@
"supervisor": "^0.11.0"
},
"engines": {
"node": ">=4.0.0"
"node": ">=6.0.0"
}
}
Loading

0 comments on commit e88dc90

Please sign in to comment.