Skip to content

Commit

Permalink
Work done fixing up visualization page.
Browse files Browse the repository at this point in the history
Wrote services to resolve analysis result urls used in visualization page.
Also started adding event handlers to svg image elements so that broken images
won't show the ugly broken image glyph. Also on sucessful image load,
redundant svg elements will be removed from each tile.

**An unfortunate side affect of this change is that the uuid is required now for all audio recording elements
shown in the visualization page** 😞
  • Loading branch information
atruskie committed Jun 7, 2015
1 parent 70c3865 commit 4c7da62
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 11 deletions.
28 changes: 25 additions & 3 deletions src/app/d3Bindings/eventDistribution/distributionVisualisation.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ angular
function updateElements() {
var imageAttrs = {
height: tilesHeight,
width: tileSizePixels
width: tileSizePixels,
};

function tileGTranslation(d, i) {
Expand Down Expand Up @@ -290,9 +290,31 @@ angular
"text-anchor": "middle",
dy: "1em"
});
newTileElements.append("image")

newTileElements
.append("image")
.attr(imageAttrs)
.attr("xlink:href", getTileImage);
.attr("xlink:href", getTileImage)
.on("error", function (datum, index) {
//console.error("SVG ERROR", arguments);
var target = d3.select(d3.event.target);

// remove the href from the image
target.attr("xlink:href", null);
})
.on("load", function () {
//console.info("SVG INFO", arguments);

// if successful, remove text (and let bg color through)
var target = d3.event.target;
var siblings = target.parentNode.childNodes;
Array.prototype.forEach.call(siblings, function (node, index) {
if (!(node instanceof SVGImageElement)) {
node.remove();
}
});
});


// remove old tiles
tileElements.exit().remove();
Expand Down
11 changes: 4 additions & 7 deletions src/app/visualize/visualize.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ angular
"Project",
"Site",
"AudioRecording",
"AnalysisResultFile",
"UserProfile",
function ($scope, $routeParams, $http, $q, _, moment, paths, constants, Project, Site, AudioRecording, UserProfile) {
function ($scope, $routeParams, $http, $q, _, moment,
paths, constants, Project, Site, AudioRecording, AnalysisResultFile, UserProfile) {

var sitesMap = {};

Expand Down Expand Up @@ -105,14 +107,9 @@ angular
return d.id;
},
getTileUrl: function(date, category, tileSizeSeconds, tileSizePixels, datum, index) {
var hourOfDay = date.getHours();

if (datum.source.id !== 188238) {
return;
}
var url = AnalysisResultFile.getLongDurationImageTile(datum.source, date, 60);

// do not attempt to load dll's for demo
var url = paths.site.root + "/assets/temp/demo/188238_" + hourOfDay + ".png";
return url;
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/baw.configuration.tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ angular.module('bawApp.configuration', ['url'])
},
bookmark: {
show: "/bookmarks/{bookmarkId}"
},
analysisResults: {
system: "/audio_recordings/{recordingId}/analysis.{format}"
}
},
links: {
Expand Down
16 changes: 16 additions & 0 deletions src/components/services/analysisResult/analysisResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
angular
.module("bawApp.services.analysisResult", [])
.factory(
"AnalysisResult",
[
"$http",
function ($http) {
var analysisResult = {};




return analysisResult;
}
]
);
122 changes: 122 additions & 0 deletions src/components/services/analysisResult/analysisResultFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/**
* This file contains hard bindings for the results produced by
* AnalysisPrograms.exe. If you are using results generated by another analysis you can
* either extend this class or use an Angular service decorator to proxy the
* methods for your own use.
*/
angular
.module("bawApp.services.analysisResultFile", [])
.constant(
"ecosoundsAnalysisResults",
{
fieldDelimiter: "_",
fileDelimiter: "__",
imageExtension: "png",
falseColorProfiles: [
"ACI-ENT-EVN",
"BGN-POW-CVR"
],
tiledTag: "Tile",
longDurationAnalysisName: "Towsey.Acoustic",
defaultResolution: 60,
fileDateFormat: "YYYYMMDD-HHmmss[Z]",
numberFormatter: function(number) {
return number;
}
}
)
.factory(
"AnalysisResultFile",
[
"$http",
"$url",
"moment",
"conf.paths",
"casingTransformers",
"ecosoundsAnalysisResults",
function ($http, $url, moment, paths, casingTransformers, ear) {
var uuidRegex = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/;

function getLongDurationImage(audioRecording, imageType) {
if (!audioRecording) {
throw new Error("Expected an object for audioRecording");
}

if (audioRecording.id === undefined || !angular.isNumber(audioRecording.id)) {
throw new Error("Expected an audio recording to have an id");
}

if (!audioRecording.uuid || !uuidRegex.test(audioRecording.uuid)) {
throw new Error("Expected an audio recording to have a uuid");
}

if (!audioRecording.recordedDate || !angular.isDate(audioRecording.recordedDate)) {
throw new Error("Expected audio recording to have a valid recorded date");
}

if (!imageType) {
imageType = ear.falseColorProfiles[0];
}

if (ear.falseColorProfiles.indexOf(imageType) === -1) {
throw new Error("Expected a known image type");
}

var fileFragment = "";
fileFragment += audioRecording.uuid;
fileFragment += ear.fieldDelimiter;
fileFragment += moment.utc(+audioRecording.recordedDate).format(ear.fileDateFormat);
fileFragment += ear.fileDelimiter;
fileFragment += imageType;
fileFragment += "." + ear.imageExtension;

var url = $url.formatUri(
paths.api.routes.analysisResults.systemAbsolute,
{
recordingId: audioRecording.id,
format: ear.imageExtension,
fileName: fileFragment,
analysisId: ear.longDurationAnalysisName
},
casingTransformers.underscore
);

return url;
}

function getLongDurationImageTile(audioRecording, tileDate, resolution, imageType) {
if (!tileDate || !angular.isDate(tileDate)) {
throw new Error("Expected a valid tile date argument");
}

if (resolution === undefined) {
resolution = ear.defaultResolution;
}

if (!angular.isNumber(resolution)) {
throw new Error("Expected a valid tile resolution");
}

var url = getLongDurationImage(audioRecording, imageType);

var tileFragment = ".";
tileFragment += ear.tiledTag;
tileFragment += ear.fieldDelimiter;
tileFragment += moment.utc(+tileDate).format(ear.fileDateFormat);
tileFragment += ear.fieldDelimiter;
tileFragment += ear.numberFormatter(resolution);

// insert before extension
var insertIndex = url.lastIndexOf("." + ear.imageExtension);

return url.substr(0, insertIndex) + tileFragment + url.substr(insertIndex);
}


return {
getLongDurationImage: getLongDurationImage,
getLongDurationImageTile: getLongDurationImageTile
};
}
]
);
158 changes: 158 additions & 0 deletions src/components/services/analysisResult/analysisResultFile.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
describe("The analysis result file service", function () {

var $url, provider, AnalysisResultFile, root;

var audioRecording = {
id: 188263,
uuid: "b03685fe-6ad5-4776-90b7-e2c271dad3fb",
recordedDate: new Date("2010-10-14T00:00:00.000+10:00")
};

beforeEach(module("rails", "url", "bawApp.services", function ($urlProvider) {
provider = $urlProvider;
}));

beforeEach(inject(["$url",
"conf.paths",
"AnalysisResultFile",
function (providedUrl, paths, providedAnalysisResultFile) {
$url = providedUrl;
root = paths.api.root;
AnalysisResultFile = providedAnalysisResultFile;
}]));

it("returns an object", function () {
expect(AnalysisResultFile).toBeObject();
});


it("can return a long duration image", function () {
var actual = AnalysisResultFile.getLongDurationImage(audioRecording);

var url = root + "/audio_recordings/188263/analysis.png?file_name=b03685fe-6ad5-4776-90b7-e2c271dad3fb_20101013-140000Z__ACI-ENT-EVN.png&analysis_id=Towsey.Acoustic";

expect(actual).toBe(url);
});

it("will validate the audio recordings argument has an integer id", function () {
expect(function () {
AnalysisResultFile.getLongDurationImage({
uuid: audioRecording.uuid,
recordedDate: audioRecording.recordedDate
});
}).toThrowError(Error, "Expected an audio recording to have an id");

expect(function () {
AnalysisResultFile.getLongDurationImage({
id: "test",
uuid: audioRecording.uuid,
recordedDate: audioRecording.recordedDate
});
}).toThrowError(Error, "Expected an audio recording to have an id");
});

it("will validate the audio recordings argument has an uuid", function () {
expect(function () {
AnalysisResultFile.getLongDurationImage({id: audioRecording.id, recordedDate: audioRecording.recordedDate});
}).toThrowError(Error, "Expected an audio recording to have a uuid");

expect(function () {
AnalysisResultFile.getLongDurationImage({
id: audioRecording.id,
uuid: "not-one",
recordedDate: audioRecording.recordedDate
});
}).toThrowError(Error, "Expected an audio recording to have a uuid");

expect(function () {
AnalysisResultFile.getLongDurationImage({
id: audioRecording.id,
uuid: "1234566789012345678901234567890123456",
recordedDate: audioRecording.recordedDate
});
}).toThrowError(Error, "Expected an audio recording to have a uuid");
});

it("will validate the audio recordings argument has a recorded date value", function () {
expect(function () {
AnalysisResultFile.getLongDurationImage({id: audioRecording.id, uuid: audioRecording.uuid});
}).toThrowError(Error, "Expected audio recording to have a valid recorded date");

expect(function () {
AnalysisResultFile.getLongDurationImage({
id: audioRecording.id,
uuid: audioRecording.uuid,
recordedDate: "2010-10-14T00:00:00.000+10:00"
});
}).toThrowError(Error, "Expected audio recording to have a valid recorded date");
});

it("can return a long duration image - of a different type", function () {
var actual = AnalysisResultFile.getLongDurationImage(audioRecording, "BGN-POW-CVR");

var url = root + "/audio_recordings/188263/analysis.png?file_name=b03685fe-6ad5-4776-90b7-e2c271dad3fb_20101013-140000Z__BGN-POW-CVR.png&analysis_id=Towsey.Acoustic";

expect(actual).toBe(url);
});

it("can return a long duration image - of a different type but only valid types", function () {
expect(function () {
AnalysisResultFile.getLongDurationImage(audioRecording, "not-a-type");
}).toThrowError(Error, "Expected a known image type");
});

it("it uses the same logic to return a long duration image tile", function () {
spyOn(AnalysisResultFile, "getLongDurationImageTile");

var actual = AnalysisResultFile.getLongDurationImageTile(audioRecording);

expect(AnalysisResultFile.getLongDurationImageTile).toHaveBeenCalled();
});

it("will validate the tile date argument is a date", function () {
expect(function () {
AnalysisResultFile.getLongDurationImageTile();
}).toThrowError(Error, "Expected a valid tile date argument");

expect(function () {
var tileDate = "2010-10-14T00:00:00.000+10:00";
AnalysisResultFile.getLongDurationImageTile(audioRecording, tileDate);
}).toThrowError(Error, "Expected a valid tile date argument");
});

it("can return a long duration image tile", function () {
var tileDate = new Date("2010-10-14T12:00:00.000+10:00");
var actual = AnalysisResultFile.getLongDurationImageTile(audioRecording, tileDate);

var url = root + "/audio_recordings/188263/analysis.png?file_name=b03685fe-6ad5-4776-90b7-e2c271dad3fb_20101013-140000Z__ACI-ENT-EVN.Tile_20101014-020000Z_60.png&analysis_id=Towsey.Acoustic";

expect(actual).toBe(url);
});

it("will validate the tile resolution argument is a number", function () {
expect(function () {
var tileDate = new Date("2010-10-14T00:00:00.000+10:00");
AnalysisResultFile.getLongDurationImageTile(audioRecording, tileDate, "not a number");
}).toThrowError(Error, "Expected a valid tile resolution");
});

it("can return a long duration image tile - of a different resolution", function () {
var tileDate = new Date("2010-10-14T12:00:00.000+10:00");
var actual = AnalysisResultFile.getLongDurationImageTile(audioRecording, tileDate, 0.02);

var url = root + "/audio_recordings/188263/analysis.png?file_name=b03685fe-6ad5-4776-90b7-e2c271dad3fb_20101013-140000Z__ACI-ENT-EVN.Tile_20101014-020000Z_0.02.png&analysis_id=Towsey.Acoustic";

expect(actual).toBe(url);
});

it("can return a long duration image tile - of a different type", function () {
var tileDate = new Date("2010-10-14T12:00:00.000+10:00");
var actual = AnalysisResultFile.getLongDurationImageTile(audioRecording, tileDate, undefined, "BGN-POW-CVR");

var url = root + "/audio_recordings/188263/analysis.png?file_name=b03685fe-6ad5-4776-90b7-e2c271dad3fb_20101013-140000Z__BGN-POW-CVR.Tile_20101014-020000Z_60.png&analysis_id=Towsey.Acoustic";

expect(actual).toBe(url);
});


});
2 changes: 1 addition & 1 deletion src/components/services/audioRecording.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ angular
// WARNING: potentially very large queries because paging is disabled
return q
.in("siteId", siteIds)
.project({include: ["id", "siteId", "durationSeconds", "recordedDate"]})
.project({include: ["id", "uuid", "siteId", "durationSeconds", "recordedDate"]})
.page.disable()
.sort({orderBy: "id"});
});
Expand Down
Loading

0 comments on commit 4c7da62

Please sign in to comment.