Skip to content

Commit b687593

Browse files
authored
Merge pull request topcoderinc#165 from billsedison/dev
Drone Series - Collision Avoidance Part 1 submission final fix
2 parents 9e11b73 + 9e1a811 commit b687593

12 files changed

+380509
-12
lines changed

README.md

+69
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,75 @@ To successfully send email from this sandbox domain you have to add the user to
5656
import `test/NFZ.postman_collection.json`
5757
it contains only endpoints for No Fly Zone endpoints
5858

59+
# Smart Location Updates
60+
I added three query parameters `returnNFZ`, `nfzFields` and `nfzLimit` to API *PUT `/drones/{id}`* for getting the array of violated No fly zones in the response data.
61+
A Sample usage to get the violated no fly zones with fields `description`, `isActive`, `isPermanent`:
62+
```
63+
curl -X PUT -H "Content-Type: application/json" -d '{
64+
"lat":38.90709540316193,
65+
"lng":-77.03920125961304
66+
}' "$URL/drones/5866f36af66a5654a0816991?returnNFZ=true&nfzFields=description,isActive,isPermanent"
67+
```
68+
The response data will contain an extra field `noFlyZones`, which contains an array of `NoFlyZone` that the drone has violated.
69+
You could specified the returned fields of `noFlyZones` by `nfzFields` parameter. If you omit the `nfzFields`, all fields except the `location` will be returned.
70+
The parameter `nfzLimit` is for limit the number of returned `NoFlyZone`s, if it is omitted, then all the `NoFlyZone`s are returned.
71+
Note: the `_id` of the `NoFlyZone` is always returned.
72+
The approach to get the array of violated no fly zones is based on the `NoFlyZoneService.search`, the steps are:
73+
1. Define the search criteria: be active, matched time, geometry is "Point" type, the point coordinate is the drone's location.
74+
```
75+
{
76+
isActive: true,
77+
matchTime: true,
78+
geometry: {
79+
type: 'Point',
80+
coordinates: drone.currentLocation,
81+
},
82+
projFields: ['circle', 'description', 'startTime', 'endTime', 'isActive', 'isPermanent', 'mission'],
83+
}
84+
```
85+
2. Call `NoFlyZoneService.search` to search no fly zones. Since we only needs to get part of the fields of `NoFlyZone`, I added a field `projFields` to the search criteria.
86+
The field `projFields` could search in MongoDB with projection, which helps to improve the performance.
87+
Additionally, the `projFields` could be overrided by `nfzFields` if it is provided.
88+
`nfzFields=description,isActive,isPermanent` will be converted to `['description', 'isActive', 'isPermanent']`.
89+
3. Retrieve the array of no fly zones from the returned object, and add the `items` field to the response of this API. It is added to the field `noFlyZones`.
90+
# Nearest Drone Updates
91+
I added another three query parameters `nearDronesMaxDist`, `nearDroneFields` and `nearDronesLimit` to API *PUT `/drones/{id}`* for getting the nearest drones.
92+
A Sample usage to get 100 nearest drones within 1000 meters:
93+
```
94+
curl -X PUT -H "Content-Type: application/json" -d '{
95+
"lat":38.90709540316193,
96+
"lng":-77.03920125961304
97+
}' "$URL/drones/5866f36af66a5654a0816991?nearDronesMaxDist=1000&nearDronesLimit=100&nearDroneFields=currentLocation,status,name,description"
98+
```
99+
The response data will contain an extra field `nearestDrones`. This field contains an array of `Drones` (100 at most) that are ordered by distance.
100+
To avoid return all fields of drones in the response data, you could specify the fields to be returned by `nearDroneFields`. The example above returns the fields `currentLocation`, `status`, `name`, `description`, `_id`.
101+
If the `nearDronesMaxDist` is omitted or 0, no nearest drones will be contained in the response data.
102+
If the `nearDronesLimit` is omitted or 0, only 1 nearest drone will be returned.
103+
If the `nearDroneFields` is omitted, all the fields of drones will be returned, which is not necessary. So this field is suggested to be provided.
104+
A new field `dist` is contained in the response data for indicating the distance (in meter) between the drone and the current drone.
105+
Note: the `_id` of `Drone` is always returned.
106+
The approach to get the array of nearest drones is based on the geospatial of MongoDB. The steps are:
107+
1. Add the `2dsphere` index to `Drone.currentLocation`, since it is not a required field, we need to specify `sparse: true`.
108+
2. Rebuild the index. Since the data are for test, we could regenerate test data for rebuilding the index for simplicity.
109+
3. As we need to add `dist` field to the response data, we use `$geoNear` instead of `$nearSphere`.
110+
Set the `$geoNear` option as
111+
```
112+
{
113+
near: {
114+
type: 'Point',
115+
coordinates: drone.currentLocation,
116+
},
117+
distanceField: 'dist',
118+
maxDistance: nearDronesMaxDist,
119+
spherical: true,
120+
query: {
121+
_id: { $ne: ObjectId(id) },
122+
},
123+
}
124+
```
125+
which means to search by `Point` and filter the current drone itself.
126+
4. Add `limit` and `$project` according to the query parameter `nearDronesLimit`, `nearDroneFields`.
127+
5. Aggregate `Drone` and add returned array to the response of this API. The array is added to the field `nearestDrones`.
59128
# env
60129
61130
you also can export those values before run(data from forum).

common/helper.js

+33
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ module.exports = {
2828
getFlatternDistance,
2929
splitQueryToArray,
3030
validateObjectId,
31+
convertQueryFieldStringToArray,
32+
convertArrayToProjectionObject,
3133
};
3234

3335
/**
@@ -239,3 +241,34 @@ function validateObjectId(id) {
239241
throw new errors.HttpStatusError(httpStatus.BAD_REQUEST, `id ${id} is not valid`);
240242
}
241243
}
244+
/**
245+
* Helper method to convert query field string like a,b,c to array ['a', 'b', 'c']
246+
* @param queryFieldString
247+
* @returns {*}
248+
*/
249+
function convertQueryFieldStringToArray(queryFieldString) {
250+
if (queryFieldString && queryFieldString.length > 0) {
251+
return queryFieldString.split(',');
252+
}
253+
return [];
254+
}
255+
/**
256+
* Helper method to convert array of field names to the projection object of Mongoose
257+
* Example:
258+
* Convert fieldArray: ['a', 'b', 'c'] to
259+
* {
260+
* a: 1
261+
* b: 1
262+
* c: 1
263+
* }
264+
*
265+
* @param fieldArray Array that contains the field name
266+
* @returns {*}
267+
*/
268+
function convertArrayToProjectionObject(fieldArray) {
269+
let projection = null;
270+
if (fieldArray && fieldArray.length > 0) {
271+
projection = Object.assign(...fieldArray.map(field => ({[field]: 1})));
272+
}
273+
return projection;
274+
}

controllers/DroneController.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,14 @@ function* currentLocations(req, res) {
9898
* update a drone location
9999
*/
100100
function* updateLocation(req, res) {
101-
const drone = yield DroneService.updateLocation(req.params.id, req.body);
101+
const nfzFields = helper.convertQueryFieldStringToArray(req.query.nfzFields);
102+
const nearDroneFields = helper.convertQueryFieldStringToArray(req.query.nearDroneFields);
103+
const returnNFZ = req.query.returnNFZ;
104+
const nfzLimit = req.query.nfzLimit;
105+
const nearDronesMaxDist = req.query.nearDronesMaxDist;
106+
const nearDronesLimit = req.query.nearDronesLimit;
107+
const drone = yield DroneService.updateLocation(req.params.id, req.body, returnNFZ,
108+
nfzFields, nfzLimit, nearDronesMaxDist, nearDroneFields, nearDronesLimit);
102109
res.json(drone);
103110
res.io.emit('dronepositionupdate', drone);
104111
}

0 commit comments

Comments
 (0)