[VIVO-1119] Add D3 based versions of the co-authorship / investigator visualisations, which can be used via the runtime.properties

This commit is contained in:
grahamtriggs 2015-10-29 15:00:44 +00:00
parent ec29715259
commit 85ad30d611
12 changed files with 1039 additions and 59 deletions

View file

@ -311,6 +311,17 @@ resource.altmetric=enabled
# #
visualization.temporal = enabled visualization.temporal = enabled
#
# The co-authorship and co-investigator graphs have two variants - the traditional Flash based view,
# and views using D3.
# The traditional views require that users have Flash installed as a plugin, whereas D3 works on any
# modern browser.
# Currently, it is not possible to choose between them from the UI. You can choose to either have
# the Flash visualizations, OR the D3 visualizations.
# If this option is not present or set to disabled, then the Flash visualizations will be used.
# If this option is present and set to enabled, then the D3 visualizations will be used.
visualization.d3 = enabled
# #
# Types of individual for which we can create proxy editors. # Types of individual for which we can create proxy editors.
# If this is omitted, defaults to http://www.w3.org/2002/07/owl#Thing # If this is omitted, defaults to http://www.w3.org/2002/07/owl#Thing

View file

@ -18,6 +18,11 @@ a.no_href_styles {
width: 67%; width: 67%;
margin-left: 33%; margin-left: 33%;
} }
.vis_stats_full {
background: #fff;
clear: right;
width: 100%;
}
.vis-tables { .vis-tables {
padding: 10px; padding: 10px;
background-color: #FFF; background-color: #FFF;
@ -27,6 +32,12 @@ a.no_href_styles {
width: 44%; width: 44%;
margin: 0 10px 10px 0; margin: 0 10px 10px 0;
} }
#chord-info-div {
border: 1px solid #000000;
background: #F5F5F5;
padding: 5px;
z-index: 10;
}
p.datatable { p.datatable {
font-size: 12px; font-size: 12px;
display: block; display: block;
@ -46,6 +57,10 @@ p.datatable {
width: 67%; width: 67%;
float: right; float: right;
} }
#sparkline-container-full {
clear: both;
width: 100%;
}
#body h1 { #body h1 {
margin: 0; margin: 0;
} }

5
productMods/js/d3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -15,6 +15,23 @@
<div class="staticPageBackground"> <div class="staticPageBackground">
<div id="${visContainerID}"> <div id="${visContainerID}">
<script type="text/javascript"> <script type="text/javascript">
<#if sparklineVO.shortVisMode>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
<#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
</#if>
function drawCoauthorsSparklineVisualization(providedSparklineImgTD) { function drawCoauthorsSparklineVisualization(providedSparklineImgTD) {
@ -49,22 +66,8 @@
maxValue: '${sparklineVO.latestRenderedPublicationYear?c}' maxValue: '${sparklineVO.latestRenderedPublicationYear?c}'
}])); }]));
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
<#else> <#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
</#if> </#if>
@ -246,7 +249,7 @@
var row = $('<tr>'); var row = $('<tr>');
sparklineImgTD = $('<td>'); sparklineImgTD = $('<td>');
sparklineImgTD.attr('id', '${sparklineContainerID}_img'); sparklineImgTD.attr('id', '${sparklineContainerID}_img');
sparklineImgTD.attr('width', '150'); sparklineImgTD.attr('width', visualizationOptions.width);
sparklineImgTD.attr('class', 'sparkline_style'); sparklineImgTD.attr('class', 'sparkline_style');
row.append(sparklineImgTD); row.append(sparklineImgTD);

View file

@ -0,0 +1,33 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<table id='${tableID}'>
<caption>
${tableCaption} <a href="${fileDownloadLink}">(.CSV ${i18n().file_capitalized})</a>
</caption>
<thead>
<tr>
<th>
${tableCollaboratorColumnName}
</th>
<th>
${tableActivityColumnName}
</th>
</tr>
</thead>
<tbody>
<#list tableContent.collaborators as collaborator>
<#if collaborator_index gt 0>
<tr>
<td>
${collaborator.collaboratorName}
</td>
<td>
${tableContent.collaborationMatrix[0][collaborator_index]}
</td>
</tr>
</#if>
</#list>
</tbody>
</table>

View file

@ -15,6 +15,23 @@
<div class="staticPageBackground"> <div class="staticPageBackground">
<div id="${visContainerID}"> <div id="${visContainerID}">
<script type="text/javascript"> <script type="text/javascript">
<#if sparklineVO.shortVisMode>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
<#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
</#if>
function drawCoInvestigatorsSparklineVisualization(providedSparklineImgTD) { function drawCoInvestigatorsSparklineVisualization(providedSparklineImgTD) {
@ -53,14 +70,6 @@
</#if> </#if>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
/* /*
This means that all the publications have unknown years & we do not need to display This means that all the publications have unknown years & we do not need to display
the sparkline. the sparkline.
@ -243,7 +252,7 @@
var row = $('<tr>'); var row = $('<tr>');
sparklineImgTD = $('<td>'); sparklineImgTD = $('<td>');
sparklineImgTD.attr('id', '${sparklineContainerID}_img'); sparklineImgTD.attr('id', '${sparklineContainerID}_img');
sparklineImgTD.attr('width', '150'); sparklineImgTD.attr('width', visualizationOptions.width);
sparklineImgTD.attr('class', 'sparkline_style'); sparklineImgTD.attr('class', 'sparkline_style');
row.append(sparklineImgTD); row.append(sparklineImgTD);

View file

@ -15,6 +15,23 @@
<div class="staticPageBackground"> <div class="staticPageBackground">
<div id="${visContainerID}"> <div id="${visContainerID}">
<script type="text/javascript"> <script type="text/javascript">
<#if sparklineVO.shortVisMode>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
<#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
</#if>
function drawGrantCountVisualization(providedSparklineImgTD) { function drawGrantCountVisualization(providedSparklineImgTD) {
@ -53,13 +70,6 @@
</#if> </#if>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
/* /*
This means that all the publications have unknown years & we do not need to display This means that all the publications have unknown years & we do not need to display
@ -236,7 +246,7 @@
var row = $('<tr>'); var row = $('<tr>');
sparklineImgTD = $('<td>'); sparklineImgTD = $('<td>');
sparklineImgTD.attr('id', '${sparklineContainerID}_img'); sparklineImgTD.attr('id', '${sparklineContainerID}_img');
sparklineImgTD.attr('width', '150'); sparklineImgTD.attr('width', visualizationOptions.width);
sparklineImgTD.attr('class', 'sparkline_style'); sparklineImgTD.attr('class', 'sparkline_style');
row.append(sparklineImgTD); row.append(sparklineImgTD);

View file

@ -0,0 +1,345 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#assign standardVisualizationURLRoot ="/visualization">
<#assign shortVisualizationURLRoot ="/vis">
<#assign ajaxVisualizationURLRoot ="/visualizationAjax">
<#assign dataVisualizationURLRoot ="/visualizationData">
<#assign egoURI ="${egoURIParam?url}">
<#assign egoCoAuthorshipDataFeederURL = '${urls.base}${dataVisualizationURLRoot}?vis=coauthorship&uri=${egoURI}&vis_mode=coauthor_network_stream&labelField=label'>
<#if egoLocalName?has_content >
<#assign coprincipalinvestigatorURL = '${urls.base}${shortVisualizationURLRoot}/investigator-network/${egoLocalName}'>
<#else>
<#assign coprincipalinvestigatorURL = '${urls.base}${shortVisualizationURLRoot}/investigator-network/?uri=${egoURI}'>
</#if>
<#assign egoCoAuthorsListDataFileURL = '${urls.base}${dataVisualizationURLRoot}?vis=coauthorship&uri=${egoURI}&vis_mode=coauthors'>
<#assign egoCoAuthorshipNetworkDataFileURL = '${urls.base}${dataVisualizationURLRoot}?vis=coauthorship&uri=${egoURI}&vis_mode=coauthor_network_download'>
<#assign googleVisualizationAPI = 'https://www.google.com/jsapi?autoload=%7B%22modules%22%3A%5B%7B%22name%22%3A%22visualization%22%2C%22version%22%3A%221%22%2C%22packages%22%3A%5B%22areachart%22%2C%22imagesparkline%22%5D%7D%5D%7D'>
<#assign coAuthorPersonLevelJavaScript = '${urls.base}/js/visualization/coauthorship/coauthorship-personlevel.js'>
<#assign commonPersonLevelJavaScript = '${urls.base}/js/visualization/personlevel/person-level.js'>
<#assign coInvestigatorIcon = '${urls.images}/visualization/coauthorship/co_investigator_icon.png'>
<script type="text/javascript" src="${googleVisualizationAPI}"></script>
<script language="JavaScript" type="text/javascript">
<!--
// -----------------------------------------------------------------------------
// Globals
var egoURI = "${egoURI}";
var unEncodedEgoURI = "${egoURIParam}";
var egoCoAuthorshipDataFeederURL = "${egoCoAuthorshipDataFeederURL}";
var egoCoAuthorsListDataFileURL = "${egoCoAuthorsListDataFileURL}";
var contextPath = "${urls.base}";
var visualizationDataRoot = "${dataVisualizationURLRoot}";
// -->
var i18nStringsCoauthorship = {
coAuthorsString: '${i18n().co_authors_capitalized}',
authorString: '${i18n().author_capitalized}',
publicationsWith: '${i18n().publications_with}',
publicationsString: "${i18n().through_today}",
coauthorsString: '${i18n().co_author_s_capitalized}'
};
var i18nStringsPersonLvl = {
fileCapitalized: '${i18n().file_capitalized}',
contentRequiresFlash: '${i18n().content_requires_flash}',
getFlashString: '${i18n().get_flash}'
};
</script>
<script type="text/javascript" src="${coAuthorPersonLevelJavaScript}"></script>
<script type="text/javascript" src="${commonPersonLevelJavaScript}"></script>
${scripts.add('<script type="text/javascript" src="${urls.base}/js/visualization/visualization-helper-functions.js"></script>')}
${scripts.add('<script type="text/javascript" src="${urls.base}/js/d3.min.js"></script>')}
${stylesheets.add('<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/personlevel/page.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/visualization.css" />')}
<#assign egoVivoProfileURL = "${urls.base}/individual?uri=${egoURI}" />
<script language="JavaScript" type="text/javascript">
$(document).ready(function(){
processProfileInformation("ego_label",
"ego_moniker",
"ego_profile_image",
jQuery.parseJSON(getWellFormedURLs("${egoURIParam}", "profile_info")));
<#if (numOfCoAuthorShips?? && numOfCoAuthorShips <= 0) || (numOfAuthors?? && numOfAuthors <= 0) >
if ($('#ego_label').text().length > 0) {
setProfileName('no_coauthorships_person', $('#ego_label').text());
}
</#if>
$.ajax({
url: "${urls.base}/visualizationAjax",
data: ({vis: "utilities", vis_mode: "SHOW_GRANTS_LINK", uri: '${egoURIParam}'}),
dataType: "json",
success:function(data){
/*
Collaboratorship links do not show up by default. They should show up only if there any data to
show on that page.
*/
if (data.numOfGrants !== undefined && data.numOfGrants > 0) {
$(".toggle_visualization").show();
}
}
});
// RENDER CHORD
var labels = [];
var uris = [];
var matrix = [];
var matrixX = 0;
<#list coAuthorshipData.collaborationMatrix as row>
matrix[matrixX] = [];
<#list row as cell>
matrix[matrixX].push(${cell?c});
</#list>
matrixX++;
</#list>
<#list coAuthorshipData.collaborators as collaborator>
labels.push("${collaborator.collaboratorName}");
uris.push("${collaborator.collaboratorURI}");
</#list>
var chord = d3.layout.chord()
.padding(0.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var width = 725;
var height = 725;
var padding = 175;
var inner_radius = Math.min(width, height) * 0.37;
var outer_radius = Math.min(width, height) * 0.39;
var fill = d3.scale.category10();
var svg = d3.select('#chord').append('svg')
.attr('width', width+padding)
.attr('height', height+padding)
.append('g').attr('transform', 'translate(' + (width+padding) / 2 + ',' + (height+padding) / 2 +')');
svg.append('g').selectAll('path').data(chord.groups).enter()
.append('path').style('fill', function(val) { return val.index == 0 ? "#000000" : fill(val.index); })
.style('stroke', function(val) { return val.index == 0 ? "#000000" : fill(val.index); })
.attr('d', d3.svg.arc().innerRadius(inner_radius).outerRadius(outer_radius))
.on('click', chord_click())
.on("mouseover", chord_hover(.05))
.on("mouseout", chord_hover(.8));
var group_ticks = function (d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(d.value / 2, d.value, d.value / 2).map(function (v) {
return {
angle: v * k + d.startAngle,
label: Math.round(d.value)
};
});
};
var chord_ticks = svg.append('g')
.selectAll('g')
.data(chord.groups)
.enter().append('g')
.selectAll('g')
.data(group_ticks)
.enter().append('g')
.attr('transform', function (d) {
return 'rotate(' + (d.angle * 180 / Math.PI - 90) + ') translate(' + outer_radius + ',0)';
});
svg.append('g')
.attr('class', 'chord')
.selectAll('path')
.data(chord.chords)
.enter().append('path')
.style('fill', function (d) { return fill(d.target.index); })
.attr('d', d3.svg.chord().radius(inner_radius))
.style('opacity', .8);
svg.append("g").selectAll(".arc")
.data(chord.groups)
.enter().append("svg:text")
.attr("dy", ".35em")
.attr("style", function(d) { return d.index == 0 ? "font-size: .75em; font-weight: bold;" : "font-size: .70em;"; } )
.attr("text-anchor", function(d) { return ((d.startAngle + d.endAngle) / 2) > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (((d.startAngle + d.endAngle) / 2) * 180 / Math.PI - 90) + ")"
+ "translate(" + (height *.40) + ")"
+ (((d.startAngle + d.endAngle) / 2) > Math.PI ? "rotate(180)" : "");
})
.text(function(d) {
return labels[d.index];
})
.on('click', chord_click())
.on("mouseover", chord_hover(.05))
.on("mouseout", chord_hover(.8));
function chord_hover(opacity) {
return function(g, i) {
if (opacity > .5) {
var chordInfoDiv = d3.select('#chord-info-div');
chordInfoDiv.style('display', 'none');
} else {
var hoverEvent = d3.event;
var topPos = hoverEvent.pageY - 60;
var leftPos = hoverEvent.pageX + 10;
var chord = d3.select('#chord').node();
var chordInfoDiv = d3.select('#chord-info-div');
var hoverMsg = labels[i] + "<br/>";
if (i > 0) {
hoverMsg += matrix[i][0] + " Joint ${i18n().publication_s_capitalized}<br/>";
} else {
hoverMsg += "${coAuthorshipData.collaboratorsCount} ${i18n().co_author_s_capitalized}<br/>";
}
chordInfoDiv.html(hoverMsg);
chordInfoDiv.style('display', 'block');
chordInfoDiv.style('position', 'absolute');
if (d3.mouse(chord)[1] > height / 2) {
topPos += 80;
}
chordInfoDiv.style('top', topPos + 'px');
if (hoverEvent.pageX > document.body.clientWidth / 2) {
leftPos = hoverEvent.pageX + 10;
} else {
leftPos = hoverEvent.pageX - (10 + chordInfoDiv.node().getBoundingClientRect().width);
}
chordInfoDiv.style('left', leftPos + 'px');
}
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
}
}
function chord_click() {
return function (g, i) {
if (i > 0) {
window.location.href = getWellFormedURLs(uris[i], "profile");
}
};
}
});
</script>
<div id="body">
<div class="sub_headings"><h2><a href="${egoVivoProfileURL}" title="${i18n().author_name}"><span id="ego_label"></span></a><br />${i18n().co_author_network} </h2></div>
<#if (numOfCoAuthorShips?? && numOfCoAuthorShips > 0) || (numOfAuthors?? && numOfAuthors > 0) >
<div class = "graphml-file-link">(<a href="${egoCoAuthorshipNetworkDataFileURL}" title="GraphML ${i18n().file}">GraphML ${i18n().file}</a>)</div>
<#else>
<#if numOfAuthors?? && numOfAuthors <= 0 >
<#assign authorsText = "multi-author" />
</#if>
<div id="no_coauthorships">${i18n().currently_no_papers_for(authorsText!)}
<a href="${egoVivoProfileURL}" title="${i18n().co_authorship}"><span id="no_coauthorships_person" class="author_name">${i18n().this_author}</span></a> ${i18n().in_the_vivo_db}
</div>
</#if>
<div class = "toggle_visualization">
<div id="coinvestigator_link_container" class="collaboratorship-link-container">
<div class="collaboratorship-icon"><a href="${coprincipalinvestigatorURL}" title="${i18n().co_investigator}"><img src="${coInvestigatorIcon}" alt="${i18n().co_investigator_icon}"/></a></div>
<div class="collaboratorship-link">
<h3><a href="${coprincipalinvestigatorURL}" title="${i18n().co_investigator_network}">${i18n().co_investigator_network_capitalized}</a></h3>
</div>
</div>
</div>
<div style="clear:both;"></div>
<#if (numOfAuthors?? && numOfAuthors > 0) >
<#else>
<span id="no_coauthorships">${i18n().no_papers_for}
<a href="${egoVivoProfileURL}" title="${i18n().co_authorship}"><span id="no_coauthorships_person" class="author_name">${i18n().this_author}</span></a> ${i18n().in_the_vivo_db}
</span>
</#if>
<#if (numOfCoAuthorShips?? && numOfCoAuthorShips > 0) || (numOfAuthors?? && numOfAuthors > 0) >
<div id="bodyPannel">
<div id="chord" style="float: right;"></div>
</div>
</#if>
<#if (numOfAuthors?? && numOfAuthors > 0) >
<#-- Sparkline -->
<div id="sparkline-container-full">
<#assign displayTable = false />
<#assign sparklineVO = egoPubSparklineVO />
<div id="publication-count-sparkline-include"><#include "personPublicationSparklineContent.ftl"></div>
<#assign sparklineVO = uniqueCoauthorsSparklineVO />
<div id="coauthor-count-sparkline-include"><#include "coAuthorshipSparklineContent.ftl"></div>
</div>
<div class="vis_stats_full">
<div class="sub_headings" id="table_heading"><h3>${i18n().tables_capitalized}</h3></div>
<div class="vis-tables">
<p id="publications_table_container" class="datatable">
<#assign tableID = "publication_data_table" />
<#assign tableCaption = "${i18n().publications_per_year} " />
<#assign tableActivityColumnName = "${i18n().publications_capitalized}" />
<#assign tableContent = egoPubSparklineVO.yearToActivityCount />
<#assign fileDownloadLink = egoPubSparklineVO.downloadDataLink />
<#include "yearToActivityCountTable.ftl">
</p>
</div>
<#if (numOfCoAuthorShips?? && numOfCoAuthorShips > 0) >
<div class="vis-tables">
<p id="coauth_table_container" class="datatable">
<#assign tableID = "coauthorships_table" />
<#assign tableCaption = "${i18n().co_authors_capitalized} " />
<#assign tableCollaboratorColumnName = "${i18n().author_capitalized}" />
<#assign tableActivityColumnName = "${i18n().publications_with}" />
<#assign tableContent = coAuthorshipData />
<#assign fileDownloadLink = uniqueCoauthorsSparklineVO.downloadDataLink />
<#include "collaboratorToActivityCountTable.ftl">
</p>
</div>
</#if>
<div style="clear:both"></div>
</div>
</#if>
</div>
<div id="chord-info-div" style="display: none;"></div>

View file

@ -0,0 +1,359 @@
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
<#assign standardVisualizationURLRoot ="/visualization">
<#assign shortVisualizationURLRoot ="/vis">
<#assign ajaxVisualizationURLRoot ="/visualizationAjax">
<#assign dataVisualizationURLRoot ="/visualizationData">
<#assign egoURI ="${egoURIParam?url}">
<#assign egoCoInvestigationDataFeederURL = '${urls.base}${dataVisualizationURLRoot}?vis=coprincipalinvestigator&uri=${egoURI}&vis_mode=copi_network_stream&labelField=label'>
<#assign coauthorshipURL = '${urls.base}${shortVisualizationURLRoot}/author-network/?uri=${egoURI}'>
<#if egoLocalName?has_content >
<#assign coauthorshipURL = '${urls.base}${shortVisualizationURLRoot}/author-network/${egoLocalName}'>
<#else>
<#assign coauthorshipURL = '${urls.base}${shortVisualizationURLRoot}/author-network/?uri=${egoURI}'>
</#if>
<#assign egoCoInvestigatorsListDataFileURL = '${urls.base}${dataVisualizationURLRoot}?vis=coprincipalinvestigator&uri=${egoURI}&vis_mode=copis'>
<#assign egoCoInvestigationNetworkDataFileURL = '${urls.base}${dataVisualizationURLRoot}?vis=coprincipalinvestigator&uri=${egoURI}&vis_mode=copi_network_download'>
<#assign coAuthorIcon = '${urls.images}/visualization/coauthorship/co_author_icon.png'>
<#assign googleVisualizationAPI = 'https://www.google.com/jsapi?autoload=%7B%22modules%22%3A%5B%7B%22name%22%3A%22visualization%22%2C%22version%22%3A%221%22%2C%22packages%22%3A%5B%22areachart%22%2C%22imagesparkline%22%5D%7D%5D%7D'>
<#assign coInvestigatorPersonLevelJavaScript = '${urls.base}/js/visualization/coPIship/coPIship-person-level.js'>
<#assign commonPersonLevelJavaScript = '${urls.base}/js/visualization/personlevel/person-level.js'>
<script type="text/javascript" src="${googleVisualizationAPI}"></script>
<script language="JavaScript" type="text/javascript">
<!--
// -----------------------------------------------------------------------------
// Globals
var egoURI = "${egoURI}";
var unEncodedEgoURI = "${egoURIParam}";
var egoCoInvestigationDataFeederURL = "${egoCoInvestigationDataFeederURL}";
var egoCoInvestigatorsListDataFileURL = "${egoCoInvestigatorsListDataFileURL}";
var contextPath = "${urls.base}";
var visualizationDataRoot = "${dataVisualizationURLRoot}";
// -->
var i18nStringsCoPi = {
coInvestigatorString: '${i18n().co_inestigators_capitalized}',
investigatorString: '${i18n().investigator_capitalized}',
grantsWithString: '${i18n().grants_with}',
grantsCapitalized: '${i18n().grant_s_capitalized}',
coInvestigatorCapitalized: '${i18n().co_investigator_s_capitalized}'
};
var i18nStringsPersonLvl = {
fileCapitalized: '${i18n().file_capitalized}',
contentRequiresFlash: '${i18n().content_requires_flash}',
getFlashString: '${i18n().get_flash}'
};
</script>
<script type="text/javascript" src="${coInvestigatorPersonLevelJavaScript}"></script>
<script type="text/javascript" src="${commonPersonLevelJavaScript}"></script>
${scripts.add('<script type="text/javascript" src="${urls.base}/js/visualization/visualization-helper-functions.js"></script>')}
${scripts.add('<script type="text/javascript" src="${urls.base}/js/d3.min.js"></script>')}
${stylesheets.add('<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/personlevel/page.css" />',
'<link rel="stylesheet" type="text/css" href="${urls.base}/css/visualization/visualization.css" />')}
<#assign loadingImageLink = "${urls.images}/visualization/ajax-loader.gif">
<#assign egoVivoProfileURL = "${urls.base}/individual?uri=${egoURI}" />
<script language="JavaScript" type="text/javascript">
$(document).ready(function(){
processProfileInformation("ego_label",
"ego_moniker",
"ego_profile_image",
jQuery.parseJSON(getWellFormedURLs("${egoURIParam}", "profile_info")));
<#if (numOfCoInvestigations?? && numOfCoInvestigations <= 0) || (numOfInvestigators?? && numOfInvestigators <= 0) >
if ($('#ego_label').text().length > 0) {
setProfileName('no_coinvestigations_person', $('#ego_label').text());
}
</#if>
$.ajax({
url: "${urls.base}/visualizationAjax",
data: ({vis: "utilities", vis_mode: "SHOW_AUTHORSHIP_LINK", uri: '${egoURIParam}'}),
dataType: "json",
success:function(data){
/*
Collaboratorship links do not show up by default. They should show up only if there any data to
show on that page.
*/
if (data.numOfPublications !== undefined && data.numOfPublications > 0) {
$(".toggle_visualization").show();
}
}
});
// RENDER CHORD
var labels = [];
var uris = [];
var matrix = [];
var matrixX = 0;
<#list coInvestigatorData.collaborationMatrix as row>
matrix[matrixX] = [];
<#list row as cell>
matrix[matrixX].push(${cell?c});
</#list>
matrixX++;
</#list>
<#list coInvestigatorData.collaborators as collaborator>
labels.push("${collaborator.collaboratorName}");
uris.push("${collaborator.collaboratorURI}");
</#list>
var chord = d3.layout.chord()
.padding(0.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var width = 725;
var height = 725;
var padding = 175;
var inner_radius = Math.min(width, height) * 0.37;
var outer_radius = Math.min(width, height) * 0.39;
var fill = d3.scale.category10();
var svg = d3.select('#chord').append('svg')
.attr('width', width+padding)
.attr('height', height+padding)
.append('g').attr('transform', 'translate(' + (width+padding) / 2 + ',' + (height+padding) / 2 +')');
svg.append('g').selectAll('path').data(chord.groups).enter()
.append('path').style('fill', function(val) { return val.index == 0 ? "#000000" : fill(val.index); })
.style('stroke', function(val) { return val.index == 0 ? "#000000" : fill(val.index); })
.attr('d', d3.svg.arc().innerRadius(inner_radius).outerRadius(outer_radius))
.on('click', chord_click())
.on("mouseover", chord_hover(.05))
.on("mouseout", chord_hover(.8));
var group_ticks = function (d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(d.value / 2, d.value, d.value / 2).map(function (v) {
return {
angle: v * k + d.startAngle,
label: Math.round(d.value)
};
});
};
var chord_ticks = svg.append('g')
.selectAll('g')
.data(chord.groups)
.enter().append('g')
.selectAll('g')
.data(group_ticks)
.enter().append('g')
.attr('transform', function (d) {
return 'rotate(' + (d.angle * 180 / Math.PI - 90) + ') translate(' + outer_radius + ',0)';
});
svg.append('g')
.attr('class', 'chord')
.selectAll('path')
.data(chord.chords)
.enter().append('path')
.style('fill', function (d) { return fill(d.target.index); })
.attr('d', d3.svg.chord().radius(inner_radius))
.style('opacity', .8);
svg.append("g").selectAll(".arc")
.data(chord.groups)
.enter().append("svg:text")
.attr("dy", ".35em")
.attr("style", function(d) { return d.index == 0 ? "font-size: .75em; font-weight: bold;" : "font-size: .70em;"; } )
.attr("text-anchor", function(d) { return ((d.startAngle + d.endAngle) / 2) > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (((d.startAngle + d.endAngle) / 2) * 180 / Math.PI - 90) + ")"
+ "translate(" + (height *.40) + ")"
+ (((d.startAngle + d.endAngle) / 2) > Math.PI ? "rotate(180)" : "");
})
.text(function(d) {
return labels[d.index];
})
.on('click', chord_click())
.on("mouseover", chord_hover(.05))
.on("mouseout", chord_hover(.8));
function chord_hover(opacity) {
return function(g, i) {
if (opacity > .5) {
var chordInfoDiv = d3.select('#chord-info-div');
chordInfoDiv.style('display', 'none');
} else {
var hoverEvent = d3.event;
var topPos = hoverEvent.pageY - 60;
var leftPos = hoverEvent.pageX + 10;
var chord = d3.select('#chord').node();
var chordInfoDiv = d3.select('#chord-info-div');
var hoverMsg = labels[i] + "<br/>";
if (i > 0) {
hoverMsg += matrix[i][0] + " Joint ${i18n().grant_s_capitalized}<br/>";
} else {
hoverMsg += "${coInvestigatorData.collaboratorsCount} ${i18n().co_investigator_s_capitalized}<br/>";
}
chordInfoDiv.html(hoverMsg);
chordInfoDiv.style('display', 'block');
chordInfoDiv.style('position', 'absolute');
if (d3.mouse(chord)[1] > height / 2) {
topPos += 80;
}
chordInfoDiv.style('top', topPos + 'px');
if (hoverEvent.pageX > document.body.clientWidth / 2) {
leftPos = hoverEvent.pageX + 10;
} else {
leftPos = hoverEvent.pageX - (10 + chordInfoDiv.node().getBoundingClientRect().width);
}
chordInfoDiv.style('left', leftPos + 'px');
}
svg.selectAll(".chord path")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("opacity", opacity);
}
}
function chord_click() {
return function (g, i) {
if (i > 0) {
window.location.href = getWellFormedURLs(uris[i], "profile");
}
};
}
});
</script>
<div id="body">
<div class="sub_headings"><h2><a href="${egoVivoProfileURL}" title="${i18n().investigator_name}"><span id="ego_label"></span></a><br />${i18n().co_investigator_network_capitalized} </h2></div>
<#if (numOfInvestigators?? && numOfInvestigators > 0) >
<#if (numOfCoInvestigations?? && numOfCoInvestigations > 0) || (numOfInvestigators?? && numOfInvestigators > 0) >
<div class = "graphml-file-link"><a href="${egoCoInvestigationNetworkDataFileURL}" title="${i18n().co_investigator}">(GraphML ${i18n().file_capitalized})</a></div>
<#else>
<#if numOfInvestigators?? && numOfInvestigators <= 0 >
<#assign investigatorsText = "multi-investigator" />
</#if>
<span id="no_coinvestigations">${i18n().currently_no_grants_for(investigatorsText!)}
<a href="${egoVivoProfileURL}" title="${i18n().investigator_name}"><span id="no_coinvestigations_person" class="investigator_name">${i18n().this_investigator}</span></a> ${i18n().in_the_vivo_db}
</span>
</#if>
<#else>
<span id="no_coinvestigations">${i18n().no_grants_for}
<a href="${egoVivoProfileURL}" title="${i18n().co_investigator}"><span id="no_coinvestigations_person" class="investigator_name">${i18n().this_investigator}</span></a> ${i18n().in_the_vivo_db}
</span>
</#if>
<div class = "toggle_visualization">
<div id="coauthorship_link_container" class="collaboratorship-link-container">
<div class="collaboratorship-icon"><a href="${coauthorshipURL}" title="${i18n().co_author}"><img src="${coAuthorIcon}" alt="${i18n().co_author_icon}"/></a></div>
<div class="collaboratorship-link">
<h3><a href="${coauthorshipURL}" title="${i18n().co_author_network}">${i18n().co_author_network}</a></h3>
</div>
</div>
</div>
<div style="clear:both;"></div>
<#if (numOfCoInvestigations?? && numOfCoInvestigations > 0) || (numOfInvestigators?? && numOfInvestigators > 0) >
<div id="bodyPannel">
<div id="chord" style="float: right;"></div>
</div>
</#if>
<#if (numOfInvestigators?? && numOfInvestigators > 0) >
<#-- Sparkline -->
<div id="sparkline-container-full">
<#assign displayTable = false />
<#assign sparklineVO = egoGrantSparklineVO />
<div id="grant-count-sparkline-include"><#include "personGrantSparklineContent.ftl"></div>
<#assign sparklineVO = uniqueCoInvestigatorsSparklineVO />
<div id="coinvestigator-count-sparkline-include"><#include "coInvestigationSparklineContent.ftl"></div>
</div>
<div class="vis_stats_full">
<div class="sub_headings" id="table_heading"><h3>${i18n().tables_capitalized}</h3></div>
<p style="float:left;font-size:.9em">${i18n().grant_info_for_all_years}&nbsp;<img class="filterInfoIcon" width="16px" height="16px" id="imageIconThree" src="${urls.images}/iconInfo.png" alt="${i18n().info_icon}" title="${i18n().grant_sparkline_note}" /></p>
<div style="clear:both"></div>
<div class="vis-tables">
<p id="grants_table_container" class="datatable">
<#assign tableID = "grant_data_table" />
<#assign tableCaption = "${i18n().grants_per_year}" />
<#assign tableActivityColumnName = "${i18n().grants_capitalized}" />
<#assign tableContent = egoGrantSparklineVO.yearToActivityCount />
<#assign fileDownloadLink = egoGrantSparklineVO.downloadDataLink />
<#include "yearToActivityCountTable.ftl">
</p>
</div>
<#if (numOfCoInvestigations?? && numOfCoInvestigations > 0) >
<div class="vis-tables">
<p id="coinve_table_container" class="datatable">
<#assign tableID = "coinvestigations_table" />
<#assign tableCaption = "${i18n().co_investigator_s_capitalized} " />
<#assign tableCollaboratorColumnName = "${i18n().investigator_capitalized}" />
<#assign tableActivityColumnName = "${i18n().grants_with}" />
<#assign tableContent = coInvestigatorData />
<#assign fileDownloadLink = uniqueCoInvestigatorsSparklineVO.downloadDataLink />
<#include "collaboratorToActivityCountTable.ftl">
</p>
</div>
</#if>
<div style="clear:both"></div>
</div>
</#if>
</div>
<div id="chord-info-div" style="display: none;"></div>

View file

@ -15,6 +15,23 @@
<div class="staticPageBackground"> <div class="staticPageBackground">
<div id="${visContainerID}"> <div id="${visContainerID}">
<script type="text/javascript"> <script type="text/javascript">
<#if sparklineVO.shortVisMode>
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
<#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
};
</#if>
function drawPubCountVisualization(providedSparklineImgTD) { function drawPubCountVisualization(providedSparklineImgTD) {
@ -50,21 +67,8 @@
maxValue: '${sparklineVO.latestRenderedPublicationYear?c}' maxValue: '${sparklineVO.latestRenderedPublicationYear?c}'
}])); }]));
var visualizationOptions = {
width: 150,
height: 60,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
<#else> <#else>
var visualizationOptions = {
width: 250,
height: 75,
color: '3399CC',
chartType: 'ls',
chartLabel: 'r'
}
</#if> </#if>
@ -247,7 +251,7 @@
var row = $('<tr>'); var row = $('<tr>');
sparklineImgTD = $('<td>'); sparklineImgTD = $('<td>');
sparklineImgTD.attr('id', '${sparklineContainerID}_img'); sparklineImgTD.attr('id', '${sparklineContainerID}_img');
sparklineImgTD.attr('width', '150'); sparklineImgTD.attr('width', visualizationOptions.width);
sparklineImgTD.attr('class', 'sparkline_style'); sparklineImgTD.attr('class', 'sparkline_style');
row.append(sparklineImgTD); row.append(sparklineImgTD);

View file

@ -5,7 +5,10 @@ package edu.cornell.mannlib.vitro.webapp.visualization.personlevel;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CoAuthorshipData; import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CoAuthorshipData;
import edu.cornell.mannlib.vitro.webapp.visualization.visutils.CollaborationDataViewHelper;
import org.apache.axis.utils.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import com.hp.hpl.jena.query.Dataset; import com.hp.hpl.jena.query.Dataset;
@ -239,7 +242,13 @@ public class PersonLevelRequestHandler implements VisualizationRequestHandler {
String standaloneTemplate = "coAuthorPersonLevel.ftl"; String standaloneTemplate = "coAuthorPersonLevel.ftl";
body.put("egoURIParam", egoURI); String property = ConfigurationProperties.getBean(vitroRequest).getProperty("visualization.d3");
if ("enabled".equalsIgnoreCase(property)) {
body.put("coAuthorshipData", new CollaborationDataViewHelper(coAuthorshipVO));
standaloneTemplate = "coAuthorPersonLevelD3.ftl";
}
body.put("egoURIParam", egoURI);
body.put("egoLocalName", UtilityFunctions.getIndividualLocalName(egoURI, vitroRequest)); body.put("egoLocalName", UtilityFunctions.getIndividualLocalName(egoURI, vitroRequest));
@ -291,6 +300,12 @@ public class PersonLevelRequestHandler implements VisualizationRequestHandler {
String standaloneTemplate = "coPIPersonLevel.ftl"; String standaloneTemplate = "coPIPersonLevel.ftl";
String property = ConfigurationProperties.getBean(vitroRequest).getProperty("visualization.d3");
if ("enabled".equalsIgnoreCase(property)) {
body.put("coInvestigatorData", new CollaborationDataViewHelper(coPIVO));
standaloneTemplate = "coPIPersonLevelD3.ftl";
}
body.put("egoGrantSparklineVO", egoGrantSparklineVO); body.put("egoGrantSparklineVO", egoGrantSparklineVO);
body.put("uniqueCoInvestigatorsSparklineVO", uniqueCopisSparklineVO); body.put("uniqueCoInvestigatorsSparklineVO", uniqueCopisSparklineVO);

View file

@ -0,0 +1,171 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.visualization.visutils;
import edu.cornell.mannlib.vitro.webapp.visualization.collaborationutils.CollaborationData;
import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Collaboration;
import edu.cornell.mannlib.vitro.webapp.visualization.valueobjects.Collaborator;
import java.util.ArrayList;
import java.util.List;
public class CollaborationDataViewHelper {
private static final int MAX_COLLABORATORS = 35;
private CollaborationData data;
private List<Collaborator> collaborators = null;
private int[][] collaborationMatrix = null;
public CollaborationDataViewHelper(CollaborationData data) {
this.data = data;
}
public int getCollaboratorsCount() {
init(MAX_COLLABORATORS);
return data.getCollaborators().size();
}
public int[][] getCollaborationMatrix() {
init(MAX_COLLABORATORS);
return collaborationMatrix;
}
public List<Collaborator> getCollaborators() {
init(MAX_COLLABORATORS);
return collaborators;
}
private synchronized void init(int max) {
if (collaborators != null) {
return;
}
collaborators = new ArrayList<>(max);
// Find the top N collaborators
// Threshold is the lowest number of activities currently in the table
// Start at maximum possible value
int threshold = Integer.MAX_VALUE;
// Iterate through each collaborator
for (Collaborator collaborator : data.getCollaborators()) {
// Only include the collaborator if it isn't the focus of the view
if (collaborator.getCollaboratorID() != data.getEgoCollaborator().getCollaboratorID()) {
// If the number of activities exceeds the threshold, it needs to be included in the top N
if (collaborator.getNumOfActivities() > threshold) {
// If we've filled the Top N
if (collaborators.size() == max - 1) {
// Remove the last (lowest) entry of the Top N
collaborators.remove(collaborators.size() - 1);
}
// Find the place to insert the new entry
int insert = collaborators.size();
while (insert > 0) {
insert--;
if (collaborators.get(insert).getNumOfActivities() > collaborator.getNumOfActivities()) {
insert++;
break;
}
}
if (insert < 0) {
// If we didn't find a place to insert, insert it at the start
collaborators.add(0, collaborator);
} else if (insert < collaborators.size()) {
// If the insert position is before the end of the list, insert at that position
collaborators.add(insert, collaborator);
} else {
// Otherwise, add to the end of the list
collaborators.add(collaborator);
}
// Update the threshold with the new lowest position entry
threshold = collaborators.get(collaborators.size() - 1).getNumOfActivities();
} else {
// If we are below the threshold, check if the top N is full
if (collaborators.size() < max - 1) {
// Top N is incomplete, so add to the end of the list
collaborators.add(collaborator);
// And record the new collaboration as the threshold
threshold = collaborator.getNumOfActivities();
}
}
}
}
// Now add the person that is the focus to the start of the list
collaborators.add(0, data.getEgoCollaborator());
// Generate a list of collaborator IDs for use in filling the matrix
List<Integer> collabIDs = new ArrayList<Integer>(collaborators.size());
for (Collaborator collaborator : collaborators) {
collabIDs.add(collaborator.getCollaboratorID());
}
// If we only want to visualize collaborations between the main focus and others, set this to false
boolean fullMatrix = true;
// Initialise the matrix
collaborationMatrix = new int[collaborators.size()][collaborators.size()];
// For every row in the matrix
for (int x = 0; x < collaborators.size(); x++) {
// Get the collaborator associated with this row
Collaborator collaboratorX = collaborators.get(x);
// Generate a list of possible collaborations for this row
List<Collaboration> possibleCollaborations = new ArrayList<>();
// Go through all of the collaborations
for (Collaboration collaboration : data.getCollaborations()) {
// Get the collaborators
Collaborator source = collaboration.getSourceCollaborator();
Collaborator target = collaboration.getTargetCollaborator();
// If the collaborator for this row is involved in the collaboration, add it to the list of possibles
if (source.getCollaboratorID() == collaboratorX.getCollaboratorID() || target.getCollaboratorID() == collaboratorX.getCollaboratorID()) {
possibleCollaborations.add(collaboration);
}
}
// For every column in the matrix
for (int y = 0; y < collaborators.size(); y++) {
// Get the collaborator associated with this column
Collaborator collaboratorY = collaborators.get(y);
// If this is the first row, first column, or we are creating a full matrix of all collaborations
if (x == 0 || y == 0 || fullMatrix) {
// Go through all of the possible collaborations
for (Collaboration collaboration : possibleCollaborations) {
// Get the collaborators
Collaborator source = collaboration.getSourceCollaborator();
Collaborator target = collaboration.getTargetCollaborator();
// If the source is the row collaborator and the target is the column collaborator
if (source.getCollaboratorID() == collaboratorX.getCollaboratorID() && target.getCollaboratorID() == collaboratorY.getCollaboratorID()) {
// Add the number of collaborations to the matrix, and stop processing collaborations
collaborationMatrix[x][y] = collaboration.getNumOfCollaborations();
break;
}
// If the source is the column collaborator and the target is the row collaborator
if (source.getCollaboratorID() == collaboratorY.getCollaboratorID() && target.getCollaboratorID() == collaboratorX.getCollaboratorID()) {
// Add the number of collaborations to the matrix, and stop processing collaborations
collaborationMatrix[x][y] = collaboration.getNumOfCollaborations();
break;
}
}
}
}
}
// Reset the activity count for the top left matrix entry (focus - focus collaboration)
collaborationMatrix[0][0] = 0;
}
}