Skip to content

Commit

Permalink
make sequence adapter actually spit!
Browse files Browse the repository at this point in the history
ref #10
  • Loading branch information
maxgrossman committed Aug 28, 2018
1 parent 12c309c commit ae31a54
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 68 deletions.
122 changes: 65 additions & 57 deletions adapters/sequence/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,14 @@ class Sequence {
return Sequence.instance;
}

addSequence (sequences, sequence) {
sequences.push({ sequenceId: uuidv4(), sequence: sequence });
addSequence (sequences, images) {
sequences.push({ sequenceId: uuidv4(), images: images });
return sequences;
}

build (source, type, minCutDist, maxCutDist, maxDelta, seqSize, userId) {
build (source, type, userId, params) {
return this.fromSource(source, type).then(images => {
const params = {
maxDist: maxCutDist,
minDist: minCutDist,
maxDelta: maxDelta,
size: seqSize
}
return this.cut(flatten(images), params).then(sequences => {
return this.cut(flatten(images), params || {}).then(sequences => {
if (userId) {
sequences.map(sequence => {
sequence.userId = userId;
Expand All @@ -48,17 +42,22 @@ class Sequence {
}

calcDelta(date, nextDate) {
return nextDate.diff(date) / 1000;
return nextDate.diff(date) / 1000; // second difference
}

// https://gist.github.com/rochacbruno/2883505
// https://www.movable-type.co.uk/scripts/latlong.html
// response is in meters
calcDistance(loc, nextLoc) {
const R = 6371e3;
const diffLat = this.toRadian(nextLoc.lat) - this.toRadian(loc.lat);
const diffLon = this.toRadian(nextLoc.lon) - this.toRadian(loc.lon);
const a = Math.sin(diffLat / 2) * Math.sin(diffLat / 2) + Math.cos(this.toRadian(loc.lat)) *
Math.cos(this.toRadian(nextLoc.lat)) * Math.sin(diffLon / 2) * Math.sin(diffLon / 2);

const R = 6371e3; // meters
const radLat1 = this.toRadian(loc.lat);
const radLat2 = this.toRadian(nextLoc.lat)
const radDiffLat = this.toRadian(nextLoc.lat - loc.lat);
const radDiffLon = this.toRadian(nextLoc.lon - loc.lon);

const a = Math.sin(radDiffLat/2) * Math.sin(radDiffLat/2) +
Math.cos(radLat1) * Math.cos(radLat2) *
Math.sin(radDiffLon/2) * Math.sin(radDiffLon/2);

const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return R * c;
}
Expand All @@ -79,7 +78,8 @@ class Sequence {
}

makeDate(tags) {
return dayjs(tags.GPSDateTime.toString());
if (tags.GPSDateTime === undefined) console.log(tags);
return dayjs(tags.GPSDateTime.toString())
}

makeLoc(tags) {
Expand All @@ -91,57 +91,65 @@ class Sequence {

meta(image) {
return exif.read(image).then(tags => {
return {
image: image,
loc: this.makeLoc(tags),
timestamp: this.makeDate(tags),
id: uuidv4()
const spatial = !tags.hasOwnProperty('GPSLongitude') && !tags.hasOwnProperty('GPSLatitude');
if (spatial) {
return {
image: image,
loc: this.makeLoc(tags),
timestamp: this.makeDate(tags),
id: uuidv4()
}
} else {
return {}
}
})
.catch(err => { throw err; });
}

toRadian(coord) {
return coord * (Math.PI / 100)
return coord * (Math.PI / 180)
}

split(metas, params) {
const sortedMetas = metas.sort((a, b) => a.timestamp - b.timestamp);
const pelIndex = sortedMetas.length - 2;
metas = metas.sort((a, b) => a.timestamp - b.timestamp);
const sequences = [];
const maxDist = params.maxDist;
const maxDelta = params.maxDelta;
const maxSize = params.maxSize;
const minDist = params.minDist;
let currentSequence = [];

sortedMetas.slice(0 , pelIndex).forEach((meta, i) => {
const partnerMeta = sortedMetas[i + 1],
distance = this.calcDistance(meta.loc, partnerMeta.loc),
tooClose = distance < minDist;
const minDist = params.minDist || 0.5; // meters, half a meter
const maxDist = params.maxDist || 300;
const maxDelta = params.maxDelta || 120; // seconds, 2 minutes a part
const maxSize = params.maxSize || 100;
let currentImages = [];

for (let i = 0, metasLength = metas.length; i < metasLength; i++) {
const meta = metas[i]
meta.id = uuidv4();

// ... if image is not too close to its partner, add it to a sequence.
if (!tooClose) {
// ... if the current sequence length matches the maximum size,
// or images are too far apart (in space or time),
// add the current sequence to the sequence map, then make a new sequence.
const delta = this.calcDelta(meta.timestamp, partnerMeta.timestamp),
needNewSequence = currentSequence.length === maxSize || distance > maxDist || delta > maxDelta;

if (needNewSequence) {
this.addSequence(sequences, currentSequence);
currentSequence = [];

}
if (i === metasLength - 1) {
currentImages.push(meta)
} else {

const partnerMeta = metas[i + 1];
const distance = this.calcDistance(meta.loc, partnerMeta.loc);
const tooClose = distance < minDist;

// add a uuid then add it to the sequence!
meta.id = uuidv4();
currentSequence.push(meta);

}
})
// ... if image is not too close to its partner, add it to a sequence.
if (!tooClose) {
// ... if the current sequence length matches the maximum size,
// or images are too far apart (in space or time),
// add the current sequence to the sequence map, then make a new sequence.
const delta = this.calcDelta(meta.timestamp, partnerMeta.timestamp);
const needNewSequence = currentImages.length === maxSize || distance > maxDist || delta > maxDelta;
if (needNewSequence) {
this.addSequence(sequences, currentImages);
currentImages = [];

}
// add a uuid then add it to the sequence!
currentImages.push(meta);
}
}
}

if (currentSequence.length > 0) this.addSequence(sequences, currentSequence);
if (currentImages.length > 0) this.addSequence(sequences, currentImages);
return sequences;
}
}
Expand Down
2 changes: 1 addition & 1 deletion db/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Database {
addSequence(sequence) {
const userId = sequence.userId;
const sequenceId = sequence.sequenceId;
const images = sequence.sequence;
const images = sequence.images;
return this
.addImages(userId, sequenceId, images)
.then((res) => res)
Expand Down
12 changes: 7 additions & 5 deletions handlers/sequence/post.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ module.exports = async (r, h) => {
const paths = r.payload;
const userId = r.query.userId;
const type = r.query.type;
const minCutDist = r.params.minDist || 0.5;
const maxCutDist = r.params.maxDist || 300;
const maxDelta = r.params.maxDelta || 120;
const sequenceSize = r.params.size || 0;
const sequences = await Sequence.build(paths, type, minCutDist, maxCutDist, maxDelta, sequenceSize, userId);
const params = {};
Object.keys(r.params).forEach(k => {
if (['maxDist', 'maxDelta', 'maxSize', 'minDist'].indexOf(k) > -1) {
params[k] = r.params[k];
}
})
const sequences = await Sequence.build(paths, type, userId, params);
Database.connect(databaseLocation);
await Promise.each(sequences, (sequence) => Database.addSequence(sequence).catch(err => { throw err; }))
Database.close();
Expand Down
2 changes: 1 addition & 1 deletion schema/sequences.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ module.exports = Joi
Joi.object().keys({
userId: Joi.string().guid({ versionO: [ 'uuidv4' ]}),
sequenceId: Joi.string().guid({ version: [ 'uuidv4' ] }),
sequence: Joi.array().items(metadata)
images: Joi.array().items(metadata)
})
);
42 changes: 38 additions & 4 deletions test/sequence/adapter.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict'

const fs = require('fs-extra');
const uuidv4 = require('uuid/v4');
const path = require('path');
const chai = require('chai');
const Joi = require('joi');
Expand All @@ -15,6 +16,14 @@ const expect = chai.expect;
Promise = require('bluebird');

describe('sequence', () => {
describe('#calcDistance', () => {
it('calculates Haverstein distance between two points', () => {
const point1 = { lat: 40, lon: -122 };
const point2 = { lat: 40.2, lon: -122.009 };
const dist = Sequence.calcDistance(point1, point2);
expect(dist).to.not.be.null
})
})
it('meta reads then selializes an image\'s exif metadata', async () => {
try {
const image = './testData/exif-gps-samples/DSCN0010.JPG';
Expand All @@ -29,19 +38,44 @@ describe('sequence', () => {

}
})
it ('given a path of images, generates a list of sequence objects', async () => {
it ('given a path of images sufficiently close together in space in time, returns sequence of same length', async () => {
try {
const paths = ['/testData/exif-gps-samples', '/testData/danbjoseph'].map(p => process.cwd() + p);
const paths = [ '/testData/danbjoseph2' ].map(p => process.cwd() + p);
const sequences = await Sequence.build(paths, 'directory');
const validation = Joi.validate(sequences, sequencesSchema);

expect(validation.value).to.be.eql(sequences)
expect(validation.error).to.be.null;
return;

expect(sequences[0].images.length).to.eql(8);

} catch (e) {
console.error(e);

}
})
.timeout(10000000)
// it('given path with images less than max size, makes one big sequence', async () => {
// try {
// const paths = ['/testData/100MSDCF'].map(p => process.cwd() + p);
// const sequences = await Sequence.build(paths, 'directory', uuidv4(), { maxSize: 10000 });
// expect(sequences.length).to.eql(1);
// } catch (e) {
// console.log(e);
// }
// })
// .timeout(10000000)
// it('given multiple image paths, makes multiple sequenecs', async () => {
// try {
// const paths = ['/testData/100MSDCF', '/testData/103MSDCF'].map(p => process.cwd() + p);
// const sequences = await Sequence.build(paths, 'directory', uuidv4(), { maxSize: 1000 });
// const validation = Joi.validate(sequences, sequencesSchema);

// expect(validation.value).to.be.eql(sequences)
// expect(validation.error).to.be.null;

// } catch (e) {
// console.log(e);
// }
// })
// .timeout(10000000)
})

0 comments on commit ae31a54

Please sign in to comment.