Skip to content

Commit

Permalink
fix(visualize) Navigating on tile click now works better
Browse files Browse the repository at this point in the history
Fixes #186 and #189
  - Audio recordings are now returned from the API in a consistent order (#189)
  - Tile navigation on `click` event changed to SVG `a` elements
    - better usability (acessibility, middle clicking, control clicking, right-clicking)
	- hover over to see link
	- pre computed links slightly faster
  - fixed a bug with tile generation behaviour (extra tiles are no longer generated)
  - tiles are also sorted when filtered (no z-indexing in svg, insertion order important)
  - also fixed bug where tiles would be prematurely cut from screen if the duration of the source audio was too short
  • Loading branch information
atruskie committed Mar 16, 2015
1 parent 6347c8c commit e89e05b
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 38 deletions.
93 changes: 57 additions & 36 deletions src/app/d3Bindings/eventDistribution/distributionVisualisation.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ angular
"d3",
"roundDate",
"TimeAxis",
function (d3, roundDate, TimeAxis) {
"$url",
"conf.paths",
function (d3, roundDate, TimeAxis, $url, paths) {
return function DistributionVisualisation(target, data, dataFunctions, uniqueId) {
// variables
var self = this,
Expand Down Expand Up @@ -82,7 +84,7 @@ angular

generateTiles();

filterTiles();
visibleTiles = filterTiles(visibleExtent, self.category);

updateElements();

Expand All @@ -98,7 +100,7 @@ angular

updateScales();

filterTiles();
visibleTiles = filterTiles(visibleExtent, self.category);

updateElements();
}
Expand All @@ -120,6 +122,7 @@ angular
function updateDataVariables(data) {
// data should be an array of items with extents
self.items = data.items;

self.nyquistFrequency = data.nyquistFrequency;
self.spectrogramWindowSize = data.spectrogramWindowSize;
self.middle = null;
Expand Down Expand Up @@ -179,21 +182,26 @@ angular
}
}

function filterTiles() {
function filterTiles(visibleExtent, category) {
var filterPadding = tileSizeSeconds * 1000;
// item filter
var f = isItemVisible.bind(null, visibleExtent),
g = isInCategory.bind(null, self.category),
// pad the filtering extent with tileSize so that recordings that have
// duration < tileSize aren't filtered out prematurely
var fExtent = [(+visibleExtent[0]) - filterPadding, (+visibleExtent[1]) + filterPadding],
f = isItemVisible.bind(null, fExtent),
g = isInCategory.bind(null, category),
h = and.bind(null, g, f);

// tile filter
var l = isTileVisible.bind(null, visibleExtent);

visibleTiles = self.items
return self.items
.filter(h)
.reduce(function (previous, current) {
var t = current.tiles.filter(l);
return previous.concat(t);
}, []);
}, [])
.sort(sortTiles);
}

function createElements() {
Expand Down Expand Up @@ -221,19 +229,13 @@ angular
.tickPadding(8);
yAxisGroup = main.append("g")
.classed("y axis", true)
.translate([0,0])
.translate([0, 0])
.call(yAxis);

updateElements();
}

function updateElements() {
var gAttrs = {
"z-index": function (d) {
return d.source.recordedDate.getDay();
}
};

var imageAttrs = {
height: tilesHeight,
width: tileSizePixels
Expand All @@ -257,10 +259,13 @@ angular
// add new tiles
var newTileElements = tileElements.enter()
.append("g")
.attr(gAttrs)
.translate(tileGTranslation)
.classed("tile", true)
.on("click", navigateToAudio);
.append("a")
.attr("xlink:href", function (d, i) {
return d.audioNavigationUrl;
});

newTileElements.append("rect")
.attr(imageAttrs);

Expand Down Expand Up @@ -319,9 +324,9 @@ angular
return dataFunctions.getCategory(d) === category;
}

function isItemVisible(visibleExtent, d) {
return dataFunctions.getLow(d) < visibleExtent[1] &&
dataFunctions.getHigh(d) >= visibleExtent[0];
function isItemVisible(filterExtent, d) {
return dataFunctions.getLow(d) < filterExtent[1] &&
dataFunctions.getHigh(d) >= filterExtent[0];
}

function and(a, b, d) {
Expand All @@ -346,20 +351,41 @@ angular

// use d3's in built range functionality to generate steps
var steps = [];
while (offset <= niceHigh) {
while (offset < niceHigh) {
var nextOffset = d3.time.second.offset(offset, tileSizeSeconds);
steps.push({
offset: offset,
offsetEnd: nextOffset,
source: current,
key: offset.toISOString() + dataFunctions.getId(current)
key: offset.toISOString() + dataFunctions.getId(current),
audioNavigationUrl: getNavigateToAudioUrl(current, offset)
});
offset = nextOffset;
}

return steps;
}

function getNavigateToAudioUrl(source, tileStart) {

var ar = source,
id = ar.id,
startOffset = (tileStart - ar.recordedDate) / 1000;

// do not allow negative indexing!
if (startOffset < 0) {
startOffset = 0;
}

// intentionally not specifying an end offset - let the listen page decide
return $url.formatUri(paths.site.ngRoutes.listen,
{
recordingId: id,
start: startOffset
});

}

function getTileLeft(d, i) {
return xScale(d.offset);
}
Expand All @@ -381,25 +407,20 @@ angular
i);

if (url) {
return url;
return url;
}

return "";
}

function navigateToAudio(datum) {
// HACK: temporary behaviour for demo
// construct url
var ar = datum.source,
id = ar.id,
startOffset = (datum.offset - ar.recordedDate) / 1000,
endOffset = startOffset + 30.0;

var url = "/listen/" + id + "?start=" + startOffset + "&end=" + endOffset;

console.warn("navigating to ", url);

//window.location = url;
/**
* Order tiles based on their date. This allows elements to be painted in the
* DOM in the right order
* @param tileA
* @param tileB
*/
function sortTiles(tileA, tileB) {
return tileA.offset - tileB.offset;
}

}
Expand Down
2 changes: 1 addition & 1 deletion src/app/listen/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead'])
// parse the start and end offsets
$routeParams.start = parseFloat($routeParams.start) || 0.0;
// warn: this converts 0 to chunk duration
$routeParams.end = parseFloat($routeParams.end) || CHUNK_DURATION_SECONDS;
$routeParams.end = parseFloat($routeParams.end) || ($routeParams.start + CHUNK_DURATION_SECONDS);
var chunkDuration = ($routeParams.end - $routeParams.start);
if (chunkDuration < 0) {

Expand Down
3 changes: 2 additions & 1 deletion src/components/services/audioRecording.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ angular
return q
.in("siteId", siteIds)
.project({include: ["id", "siteId", "durationSeconds", "recordedDate"]})
.page.disable();
.page.disable()
.sort({orderBy: "id"});
});

return $http.post(filterUrl, query.toJSON());
Expand Down

0 comments on commit e89e05b

Please sign in to comment.