Files
pfosi-looking-api/models/geocache.js
2018-03-03 13:07:07 -05:00

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);
});
}
};