429 lines
9.7 KiB
JavaScript
429 lines
9.7 KiB
JavaScript
const mongoose = require('mongoose');
|
|
const GoogleMaps = require('@google/maps').createClient({
|
|
key: 'AIzaSyCvpBGztvxtRUNigOW9f0GXVRWlukJZsps'
|
|
});
|
|
|
|
var logger = require('../modules/logger');
|
|
|
|
const Schema = mongoose.Schema;
|
|
|
|
const GeocacheSchema = new Schema({
|
|
key: {
|
|
type: String,
|
|
required: true,
|
|
unique: true
|
|
},
|
|
formatted: {
|
|
type: String,
|
|
required: true
|
|
},
|
|
loc: {
|
|
type: {
|
|
type: String,
|
|
default: 'Point'
|
|
},
|
|
coordinates: [{
|
|
type: Number,
|
|
default: [0, 0]
|
|
}]
|
|
},
|
|
georesult: {
|
|
type: Schema.Types.Mixed
|
|
}
|
|
});
|
|
|
|
const Conversion = {
|
|
kilometersToMeters: (distance) => {
|
|
return parseFloat(distance * 1000);
|
|
},
|
|
|
|
kilometersToMiles: (distance) => {
|
|
return parseFloat(distance / 1.60934);
|
|
},
|
|
|
|
kilometersToNauticalMiles: (distance) => {
|
|
return parseFloat(distance * 0.539957);
|
|
},
|
|
|
|
metersToKilometers: (distance) => {
|
|
return parseFloat(distance / 1000);
|
|
},
|
|
|
|
metersToMiles: (distance) => {
|
|
return parseFloat(distance / 1609.34);
|
|
},
|
|
|
|
milesToKilometers: (distance) => {
|
|
return parseFloat(distance * 1.60934);
|
|
},
|
|
|
|
milesToMeters: (distance) => {
|
|
return parseFloat(distance * 1609.34);
|
|
},
|
|
|
|
milesToNauticalMiles: (distance) => {
|
|
return parseFloat(distance * 0.868976);
|
|
},
|
|
|
|
nauticalMilesToMeters: (distance) => {
|
|
return parseFloat(distance / 0.868976);
|
|
},
|
|
|
|
nauticalMilesToMiles: (distance) => {
|
|
return parseFloat(distance / 0.868976);
|
|
},
|
|
|
|
nauticalMilesToKilometers: (distance) => {
|
|
return parseFloat(distance * 1.852000674128);
|
|
}
|
|
};
|
|
|
|
GeocacheSchema.index({ name: 1, loc: '2dsphere' });
|
|
|
|
const GeocacheModel = mongoose.model('geocache', GeocacheSchema);
|
|
|
|
/*function distanceBetween (geoJSON1, geoJSON2, unit = 'mi') {
|
|
var radlat1 = Math.PI * geoJSON1.coordinates[1]/180;
|
|
var radlat2 = Math.PI * geoJSON2.coordinates[1]/180;
|
|
var theta = geoJSON1.coordinates[0] - geoJSON2.coordinates[0];
|
|
var radtheta = Math.PI * theta/180;
|
|
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
|
|
dist = Math.acos(dist);
|
|
dist = dist * 180/Math.PI;
|
|
dist = dist * 60 * 1.1515; // miles between
|
|
if (unit == "km") { dist = Conversion.metersToKilometers(Conversion.milesToMeters(dist)); }
|
|
if (unit == "m") { dist = dist * 1.609344; }
|
|
if (unit == "n") { dist = dist * 0.8684; }
|
|
return dist;
|
|
}*/
|
|
|
|
function queryGeodataApi (query, callback) {
|
|
GoogleMaps.geocode({
|
|
address: query
|
|
}, function(err, response) {
|
|
if (err) {
|
|
console.error('[GeocacheModel<<getGeoData>>] Address Geocoding Error', { address: query, response: response });
|
|
callback(null, err, null);
|
|
}
|
|
|
|
if (response.json && Array.isArray(response.json.results)) {
|
|
var data = {
|
|
key: sanitizeNameForKey(query),
|
|
formatted: response.json.results[0].formatted_address,
|
|
georesult: response.json.results[0],
|
|
loc: {
|
|
type: 'Point',
|
|
coordinates: [
|
|
response.json.results[0].geometry.location.lng,
|
|
response.json.results[0].geometry.location.lat
|
|
]
|
|
}
|
|
};
|
|
callback(null, null, data);
|
|
}
|
|
});
|
|
}
|
|
|
|
function sanitizeNameForKey (name) {
|
|
var key = name.replace(/[.,/#!$%^&*;:{}=\-_`~()]/g,'');
|
|
key = key.replace(/\s{2,}/g,' ');
|
|
key = key.trim();
|
|
key = key.toLowerCase();
|
|
return key;
|
|
}
|
|
|
|
module.exports = {
|
|
|
|
create: (e, geodata) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
var geocacheInstance = new GeocacheModel(geodata);
|
|
geocacheInstance.save((err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('create', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('create', err, null);
|
|
});
|
|
},
|
|
|
|
conversion: Conversion,
|
|
|
|
find: (e, searchText) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.findOne({ key: sanitizeNameForKey(searchText) }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('find', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('find', err, null);
|
|
});
|
|
},
|
|
|
|
findLike: (e, searchText) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.find({ key: new RegExp('.*' + sanitizeNameForKey(searchText) + '.*', "i") }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('findLike', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('findLike', err, null);
|
|
});
|
|
},
|
|
|
|
findNear: (e, lng, lat, distance) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
var point = {
|
|
type: 'Point',
|
|
coordinates: [ lng, lat ]
|
|
};
|
|
|
|
var opts = {
|
|
spherical: true,
|
|
maxDistance: Conversion.milesToMeters(distance)
|
|
};
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.geoNear(point, opts, (err, results) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (results) {
|
|
resolve(results);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('findNear', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('findNear', err, null);
|
|
});
|
|
},
|
|
|
|
getGeo: (e, id) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.findById(id, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('getGeos', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('getGeos', err, null);
|
|
});
|
|
},
|
|
|
|
getGeos: (e) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.find({}, (err, results) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (results) {
|
|
resolve(results);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('getGeos', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('getGeos', err, null);
|
|
});
|
|
},
|
|
|
|
getGeoJSON: (e, searchText) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
queryGeodataApi(searchText, (f, err, geodata) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (geodata) {
|
|
resolve(geodata.loc);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('getGeoJSON', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('getGeoJSON', err, null);
|
|
});
|
|
},
|
|
|
|
getGeoJSONFromCache: (e, searchText) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.findOne({ key: sanitizeNameForKey(searchText) }, (err, result) => {
|
|
if (err || !result) {
|
|
queryGeodataApi(searchText, (f, err, geodata) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (geodata) {
|
|
let geocacheInstance = new GeocacheModel(geodata);
|
|
geocacheInstance.save((err, result) => {
|
|
if (err) {
|
|
logger.error('[Geocache::getGeoJSON] There was an error creating the GeoJSON entry.', { err: err });
|
|
}
|
|
|
|
logger.debug()
|
|
});
|
|
resolve(geodata.loc);
|
|
}
|
|
});
|
|
} else if (result) {
|
|
resolve(result.loc);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('getGeoJSONFromCache', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('getGeoJSONFromCache', err, null);
|
|
});
|
|
},
|
|
|
|
populateFormatted: (e) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.find({}, (err, results) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (results) {
|
|
for (let i = 0; i < results.length; i++) {
|
|
if (!results[i].formatted) {
|
|
results[i].formatted = results[i].georesult.formatted_address;
|
|
GeocacheModel.findByIdAndUpdate(results[i]._id, { $set: results[i] }, (err, result) => {
|
|
if (err) logger.error('There was an error populating the geocache formatted address.');
|
|
if (result) logger.log('The geocache entry was updated');
|
|
});
|
|
}
|
|
}
|
|
|
|
resolve({ status: 'OK', message: 'The geocache entries have been updated.'});
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('populateFormattedAddresses', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('populateFormattedAddresses', err, null);
|
|
});
|
|
},
|
|
|
|
populateKeys: (e) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.find({}, (err, results) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (results) {
|
|
for (let i = 0; i < results.length; i++) {
|
|
if (!results[i].key) {
|
|
results[i].key = sanitizeNameForKey(results[i].name);
|
|
GeocacheModel.findByIdAndUpdate(results[i]._id, { $set: results[i] }, (err, result) => {
|
|
if (err) logger.error('There was an error populating the geocache entry key.');
|
|
if (result) logger.log('The geocache entry was updated');
|
|
});
|
|
}
|
|
}
|
|
|
|
resolve({ status: 'OK', message: 'The geocache entries have been updated.'});
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('populateKeys', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('populateKeys', err, null);
|
|
});
|
|
},
|
|
|
|
update: (e, id, geodata) => {
|
|
var cb = typeof e === 'object' && e.emit ? e.emit.bind(e) : e;
|
|
const promise = new Promise((resolve, reject) => {
|
|
GeocacheModel.findByIdAndUpdate(id, { $set: geodata }, (err, result) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
if (result) {
|
|
resolve(result);
|
|
}
|
|
});
|
|
});
|
|
|
|
promise.then((result) => {
|
|
cb('update', null, result);
|
|
})
|
|
.catch((err) => {
|
|
cb('update', err, null);
|
|
});
|
|
}
|
|
}; |