Skip to content

Commit c20a249

Browse files
c-wtimfpark
authored andcommitted
Add ability to query bounding box of features by id (#2)
* Add endpoint to get features by id(s) * Add option to get feature bounding box
1 parent 6735c54 commit c20a249

File tree

5 files changed

+65
-10
lines changed

5 files changed

+65
-10
lines changed

controllers/features.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ exports.upsert = function(req, res) {
1818
});
1919
});
2020
};
21+
*/
2122

2223
exports.getById = function(req, res) {
23-
services.activities.get(req.params.userId, req.params.activityId, function(err, activity) {
24+
let query = {
25+
id: req.query.id.split(','),
26+
include: req.query.include
27+
};
28+
29+
services.features.getById(query, (err, features) => {
2430
if (err) return common.utils.handleError(res, err);
2531

26-
services.activities.toJsonApi(activity, function(err, activityJson) {
27-
if (err) return common.utils.handleError(res, err);
28-
res.send(activityJson);
29-
});
32+
res.send({ "features": features });
3033
});
3134
};
32-
*/
3335

3436
exports.getByPoint = function(req, res) {
3537
let query = {

server.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ app.use(cors());
1515
app.use(morgan("combined", { "stream": log.stream }));
1616
app.use(bodyParser.json({ limit: '50mb' }));
1717

18+
app.get('/features/id/:id', controllers.features.getById);
1819
app.get('/features/bbox/:north/:west/:south/:east', controllers.features.getByBoundingBox);
1920
app.get('/features/point/:latitude/:longitude', controllers.features.getByPoint);
2021
app.get('/features/name/:name', controllers.features.getByName);

services/features.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,52 @@ function executeQuery(query, callback) {
3333
}
3434

3535
function getById(query, callback) {
36-
let getQuery = `SELECT ${buildQueryColumns(query)} FROM features WHERE id = ${escapeSql(query.id)}`;
36+
const ids = query.id.constructor === Array ? query.id : [query.id];
37+
const getQuery = `SELECT ${buildQueryColumns(query)} FROM features WHERE id IN (${escapeSql(ids.join(','))})`;
3738
executeQuery(getQuery, (err, rows) => {
3839
if (err) return callback(err);
3940
if (!rows || rows.length === 0) return callback(null, null);
4041

41-
return callback(null, rows[0]);
42+
return callback(null, rows);
4243
});
4344
}
4445

46+
function parseBoundingBox(bbox_geo_json) {
47+
const match = /^POLYGON\(\((.*)\)\)$/.exec(bbox_geo_json)
48+
if (!match || match.length !== 2) {
49+
return null;
50+
}
51+
52+
const coords = match[1].split(',').map(point => point.split(' '));
53+
if (coords.length !== 5 || coords.some(point => point.length !== 2)) {
54+
return null;
55+
}
56+
57+
const minX = parseFloat(coords[0][0]);
58+
const minY = parseFloat(coords[0][1]);
59+
const maxX = parseFloat(coords[2][0]);
60+
const maxY = parseFloat(coords[2][1]);
61+
62+
return [
63+
maxY, // north
64+
minX, // west
65+
minY, // south
66+
maxX // east
67+
];
68+
}
69+
4570
function rowToFeature(row, columns) {
4671
if (!row) return;
4772

4873
row['createdAt'] = row['created_at'];
4974
row['updatedAt'] = row['updated_at'];
5075
if (row['hull_geo_json']) row['hull'] = JSON.parse(row['hull_geo_json']);
76+
if (row['bbox_geo_json']) row['bbox'] = parseBoundingBox(row['bbox_geo_json']);
5177

5278
delete row['created_at'];
5379
delete row['updated_at'];
5480
delete row['hull_geo_json'];
81+
delete row['bbox_geo_json'];
5582

5683
return row;
5784
}
@@ -74,6 +101,7 @@ function buildQueryColumns(query) {
74101
if (query.include) {
75102
let includeArray = query.include.split(',');
76103

104+
if (includeArray.indexOf('bbox') !== -1) queryColumns += ',ST_AsText(ST_Envelope(hull)) as bbox_geo_json';
77105
if (includeArray.indexOf('hull') !== -1) queryColumns += ',ST_AsGeoJSON(hull) as hull_geo_json';
78106
if (includeArray.indexOf('properties') !== -1) queryColumns += ',properties';
79107
}

services/featuresCosmosDb.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,13 @@ function init(callback) {
113113
}
114114

115115
function getById(query, callback) {
116-
let getQuery = `SELECT ${buildQueryColumns(query)} FROM features WHERE id = ${escapeSql(query.id)}`;
116+
const ids = query.id.constructor === Array ? query.id : [query.id];
117+
const getQuery = `SELECT ${buildQueryColumns(query)} FROM features WHERE id IN (${escapeSql(ids.join(','))})`;
117118
executeQuery(getQuery, (err, rows) => {
118119
if (err) return callback(err);
119120
if (!rows || rows.length === 0) return callback(null, null);
120121

121-
return callback(null, rows[0]);
122+
return callback(null, rows);
122123
});
123124
}
124125

test/functional/features.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,27 @@ describe('features endpoint', function() {
108108
done();
109109
});
110110
});
111+
112+
it('can get features by id', function(done) {
113+
request.get(`${featuresEndpoint}/id/fake-3830198`, {
114+
headers: {
115+
Authorization: "Bearer " + fixtures.accessToken
116+
},
117+
json: true
118+
}, function(err, resp) {
119+
assert(!err);
120+
assert.equal(resp.statusCode, HttpStatus.OK);
121+
122+
assert(resp.body.features);
123+
assert(resp.body.features.length > 0);
124+
125+
let feature = resp.body.features[0];
126+
127+
assert.equal(feature.id, fixtures.feature.id);
128+
assert.equal(feature.name, fixtures.feature.name);
129+
assert.equal(feature.layer, fixtures.feature.layer);
130+
131+
done();
132+
});
133+
});
111134
});

0 commit comments

Comments
 (0)