From 8b456388dc547f894d1ec17d769710f64f056929 Mon Sep 17 00:00:00 2001 From: Eike Cochu <eike@cochu.com> Date: Thu, 24 Mar 2016 17:09:46 +0100 Subject: [PATCH] added explorer topic hover series highlight --- .../main/java/de/vipra/cmd/lda/Analyzer.java | 6 +- vipra-ui/app/html/explorer.html | 4 +- vipra-ui/app/js/app.js | 2 +- vipra-ui/app/js/config.js | 2 +- vipra-ui/app/js/controllers.js | 124 ++++++++++++------ vipra-ui/app/js/directives.js | 2 +- vipra-ui/app/js/factories.js | 16 ++- vipra-ui/app/js/helpers.js | 2 +- vipra-ui/app/less/app.less | 6 +- .../java/de/vipra/util/model/TopicWord.java | 7 +- 10 files changed, 111 insertions(+), 60 deletions(-) diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java index f3398bb1..b8b3d78a 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/lda/Analyzer.java @@ -258,10 +258,10 @@ public class Analyzer { // calculate topic sequence relevance final int sequenceSize = windowIndex.windowSize(idxSeq); - double seqTopicDistribution = 0; + double seqTopicDistributionSum = 0; for (int idxArticle = sequenceOffset; idxArticle < sequenceOffset + sequenceSize; idxArticle++) - seqTopicDistribution += topicDistributions[idxArticle][idxTopic]; - final double relevance = seqTopicDistribution / sequenceSize; + seqTopicDistributionSum += topicDistributions[idxArticle][idxTopic]; + final double relevance = seqTopicDistributionSum / sequenceSize; // create sequence final SequenceFull newSequenceFull = new SequenceFull(); diff --git a/vipra-ui/app/html/explorer.html b/vipra-ui/app/html/explorer.html index ae00c9a1..69563f7f 100644 --- a/vipra-ui/app/html/explorer.html +++ b/vipra-ui/app/html/explorer.html @@ -21,7 +21,7 @@ <span class="glyphicon glyphicon-remove-circle searchclear" ng-click="search=''"></span> </div> <ul class="list-unstyled topic-choice"> - <li ng-repeat="topic in topics | orderBy:explorerModels.sorttopics:explorerModels.sortdir | filter:search"> + <li ng-repeat="topic in topics | orderBy:explorerModels.sorttopics:explorerModels.sortdir | filter:search" ng-mouseenter="highlightSeries(topic.id, true)" ng-mouseleave="highlightSeries(topic.id, false)"> <div class="checkbox checkbox-condensed" ng-class="{selected:topic.selected}"> <span class="valuebar" ng-style="{width:topic.topicCurrValue}"></span> <input type="checkbox" ng-model="topic.selected" ng-attr-id="{{::topic.id}}" ng-change="redrawGraph()"> @@ -58,7 +58,7 @@ <a class="btn btn-sm btn-default" ng-model="explorerModels.chartstack" bs-radio="'percent'">Percent</a> </div> </div> - <div class="chart" highcharts="topicSeq"></div> + <div id="topicRelChart" class="chart" highcharts="topicSeq"></div> <div class="message text-muted" ng-show="!topicsSelected">No topic selected</div> </div> </div> diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js index 9b0ad989..8d5da3d6 100644 --- a/vipra-ui/app/js/app.js +++ b/vipra-ui/app/js/app.js @@ -132,4 +132,4 @@ }]); -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/config.js b/vipra-ui/app/js/config.js index 8ca1c7df..f61fc16e 100644 --- a/vipra-ui/app/js/config.js +++ b/vipra-ui/app/js/config.js @@ -13,4 +13,4 @@ restUrl: '/vipra/rest' }; -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js index f0975308..2c10e42e 100644 --- a/vipra-ui/app/js/controllers.js +++ b/vipra-ui/app/js/controllers.js @@ -20,15 +20,15 @@ topicModel: null }; - if(localStorage.topicModel) { + if (localStorage.topicModel) { try { var topicModel = JSON.parse(localStorage.topicModel); TopicModelFactory.get({ id: topicModel.id }, function(data) { - $scope.rootModels.topicModel = data; + $scope.rootModels.topicModel = data; }); - } catch(e) {} + } catch (e) {} } TopicModelFactory.query({ @@ -59,14 +59,14 @@ function($scope, $location, ArticleFactory, TopicFactory, SearchFactory) { // page was reloaded, choose topic model - if(!$scope.rootModels.topicModel) + if (!$scope.rootModels.topicModel) $scope.chooseTopicModel(); $scope.search = $location.search().query; $scope.$watch('rootModels.topicModel', function() { - if(!$scope.rootModels.topicModel) return; - + if (!$scope.rootModels.topicModel) return; + ArticleFactory.query({ topicModel: $scope.rootModels.topicModel.id, limit: 3, @@ -153,7 +153,9 @@ $scope.type = $stateParams.type; $scope.options = { nodes: { - font: { size: 14 }, + font: { + size: 14 + }, shape: 'dot', borderWidth: 0 }, @@ -162,7 +164,9 @@ highlight: '#f00' } }, - layout: { randomSeed: 1 }, + layout: { + randomSeed: 1 + }, physics: { barnesHut: { springConstant: 0.008, @@ -187,7 +191,7 @@ // if the topic model is not set, the page was refreshed // set to true, id of current node decides topic model - if(!$scope.rootModels.topicModel) + if (!$scope.rootModels.topicModel) $scope.rootModels.topicModel = true; // get root node @@ -210,7 +214,7 @@ $scope.graph.on('doubleClick', $scope.open); // take topic model from node - if(!angular.isObject($scope.rootModels.topicModel)) + if (!angular.isObject($scope.rootModels.topicModel)) $scope.rootModels.topicModel = data.topicModel; }, function(err) { $scope.errors = err; @@ -228,7 +232,9 @@ shape: shape || 'dot', color: { background: color, - highlight: { background: color } + highlight: { + background: color + } } }; }; @@ -270,11 +276,17 @@ if (ids.hasOwnProperty(current.id)) { if (edgeExists(ids[current.id], node.id)) continue; - newEdges.push({ from: ids[current.id], to: node.id }); + newEdges.push({ + from: ids[current.id], + to: node.id + }); addEdge(ids[current.id], node.id); } else { newNodes.push(nodeFunction(current)); - newEdges.push({ from: id, to: node.id }); + newEdges.push({ + from: id, + to: node.id + }); addEdge(id, node.id); } } @@ -325,11 +337,13 @@ $scope.open = function(props) { $timeout.cancel(selectTimeout); var node = $scope.nodes.get(props.nodes[0]); - $state.transitionTo(node.show, { id: node.dbid }); + $state.transitionTo(node.show, { + id: node.dbid + }); }; $scope.$watch('rootModels.topicModel', function(newVal) { - if($scope.rootNode && $scope.rootNode.topicModel.id !== newVal.id) + if ($scope.rootNode && $scope.rootNode.topicModel.id !== newVal.id) $state.transitionTo('index'); }); } @@ -339,7 +353,7 @@ function($scope, $templateCache, $timeout, TopicFactory) { // page was reloaded, choose topic model - if(!$scope.rootModels.topicModel) + if (!$scope.rootModels.topicModel) $scope.chooseTopicModel(); var avgMax = 0, @@ -358,14 +372,17 @@ }; $scope.$watch('rootModels.topicModel', function() { - if(!$scope.rootModels.topicModel) return; + if (!$scope.rootModels.topicModel) return; TopicFactory.query({ fields: 'name,sequences,avgRelevance,varRelevance,risingRelevance,fallingRelevance,risingDecayRelevance', topicModel: $scope.rootModels.topicModel.id }, function(data) { $scope.topics = data; - var colors = randomColor({ count: $scope.topics.length, seed: 1 }); + var colors = randomColor({ + count: $scope.topics.length, + seed: 1 + }); for (var i = 0, t; i < $scope.topics.length; i++) { t = $scope.topics[i]; t.color = colors[i]; @@ -414,10 +431,12 @@ } series.push({ + id: topic.id, name: topic.name, data: relevances, color: topic.color, - topic: topic + topic: topic, + zIndex: i + 1 }); } } @@ -450,15 +469,35 @@ return (percent * 100) + '%'; }; + var topicRelChartElement = $('#topicRelChart'); + $scope.highlightSeries = function(id, toggle) { + if (!$scope.topicsSelected) return; + var highcharts = topicRelChartElement.highcharts(); + if (!highcharts) return; + var series = highcharts.get(id); + if (!series) return; + + if (toggle) { + series.onMouseOver(); + series.group.zIndexOrig = series.group.zIndex; + series.group.zIndexSetter(9999, 'zIndex'); + series.graph.attr('stroke', '#000000').attr('stroke-dasharray', '5,5'); + } else { + series.onMouseOut(); + series.group.zIndexSetter(series.group.zIndexOrig, 'zIndex'); + series.graph.attr('stroke', series.color).attr('stroke-dasharray', ''); + } + }; + $scope.$watchGroup(['explorerModels.seqstyle', 'explorerModels.chartstyle', 'explorerModels.chartstack'], $scope.redrawGraph); $scope.$watch('explorerModels.sorttopics', function() { - if(!$scope.topics) return; + if (!$scope.topics) return; $timeout(function() { - for(var i = 0; i < $scope.topics.length; i++) + for (var i = 0; i < $scope.topics.length; i++) $scope.topics[i].topicCurrValue = $scope.topicCurrValue($scope.topics[i]); - }, 100); + }, 0); }); } ]); @@ -474,7 +513,7 @@ function($scope, $state, $location, ArticleFactory) { // page was reloaded, choose topic model - if(!$scope.rootModels.topicModel && $state.current.name === 'articles') + if (!$scope.rootModels.topicModel && $state.current.name === 'articles') $scope.chooseTopicModel(); $scope.articlesIndexModels = { @@ -485,7 +524,7 @@ }; $scope.$watchGroup(['articlesIndexModels.page', 'articlesIndexModels.sortkey', 'articlesIndexModels.sortdir', 'rootModels.topicModel'], function() { - if(!$scope.rootModels.topicModel) return; + if (!$scope.rootModels.topicModel) return; ArticleFactory.query({ skip: ($scope.articlesIndexModels.page - 1) * $scope.articlesIndexModels.limit, @@ -523,15 +562,19 @@ $scope.articleModified = Vipra.formatDateTime($scope.article.modified); // take topic model from article - if(!angular.isObject($scope.rootModels.topicModel)) + if (!angular.isObject($scope.rootModels.topicModel)) $scope.rootModels.topicModel = data.topicModel; // calculate percentage share if ($scope.article.topics) { var topicShareSeries = [], topics = $scope.article.topics, - maximum = { y: 0 }, - colors = randomColor({ count: $scope.article.topics.length }); + maximum = { + y: 0 + }, + colors = randomColor({ + count: $scope.article.topics.length + }); for (var i = 0, d; i < topics.length; i++) { d = { name: topics[i].topic.name, @@ -563,7 +606,7 @@ }); $scope.$watch('rootModels.topicModel', function(newVal) { - if($scope.article && $scope.article.topicModel.id !== newVal.id) + if ($scope.article && $scope.article.topicModel.id !== newVal.id) $state.transitionTo('index'); }); } @@ -580,7 +623,7 @@ function($scope, $state, $location, TopicFactory) { // page was reloaded, choose topic model - if(!$scope.rootModels.topicModel && $state.current.name === 'topics') + if (!$scope.rootModels.topicModel && $state.current.name === 'topics') $scope.chooseTopicModel(); $scope.topicsIndexModels = { @@ -591,7 +634,7 @@ }; $scope.$watchGroup(['topicsIndexModels.page', 'topicsIndexModels.sortkey', 'topicsIndexModels.sortdir', 'rootModels.topicModel'], function() { - if(!$scope.rootModels.topicModel) return; + if (!$scope.rootModels.topicModel) return; TopicFactory.query({ topicModel: $scope.rootModels.topicModel.id, @@ -630,7 +673,7 @@ $scope.topicModified = Vipra.formatDateTime($scope.topic.modified); // take topic model from topic - if(!angular.isObject($scope.rootModels.topicModel)) + if (!angular.isObject($scope.rootModels.topicModel)) $scope.rootModels.topicModel = data.topicModel; $timeout(function() { @@ -652,7 +695,10 @@ } // highcharts configuration - $scope.topicSeq = areaRelevanceChart([{ name: $scope.topic.name, data: relevances }], $scope.topicsShowModels.chartstyle); + $scope.topicSeq = areaRelevanceChart([{ + name: $scope.topic.name, + data: relevances + }], $scope.topicsShowModels.chartstyle); }; $scope.startRename = function() { @@ -673,10 +719,10 @@ $scope.isRename = false; // if topic list of parent view is loaded, replace name by new name - if($scope.$parent.topics) { - for(var i = 0, topic; i < $scope.$parent.topics.length; i++) { + if ($scope.$parent.topics) { + for (var i = 0, topic; i < $scope.$parent.topics.length; i++) { topic = $scope.$parent.topics[i]; - if(topic.id === data.id) { + if (topic.id === data.id) { break; } } @@ -701,7 +747,7 @@ $scope.$watch('topicsShowModels.chartstyle', $scope.redrawGraph); $scope.$watchGroup(['sequenceId'], function() { - if(!$scope.sequenceId) return; + if (!$scope.sequenceId) return; SequenceFactory.get({ id: $scope.sequenceId, @@ -714,7 +760,7 @@ }); $scope.$watch('rootModels.topicModel', function(newVal) { - if($scope.topic && $scope.topic.topicModel.id !== newVal.id) + if ($scope.topic && $scope.topic.topicModel.id !== newVal.id) $state.transitionTo('index'); }); } @@ -845,6 +891,8 @@ allowPointSelect: true, states: { hover: { + color: '#ff0000', + lineWidth: 4, halo: { size: 9, attributes: { @@ -892,4 +940,4 @@ }; } -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js index ad6d4f1b..c1db0a2f 100644 --- a/vipra-ui/app/js/directives.js +++ b/vipra-ui/app/js/directives.js @@ -214,4 +214,4 @@ }; }]); -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js index 97887862..e4076cbd 100644 --- a/vipra-ui/app/js/factories.js +++ b/vipra-ui/app/js/factories.js @@ -11,14 +11,22 @@ app.factory('ArticleFactory', ['$resource', function($resource) { return $resource(Vipra.config.restUrl + '/articles/:id', {}, { - similar: { isArray: true, url: Vipra.config.restUrl + '/articles/:id/similar' } + similar: { + isArray: true, + url: Vipra.config.restUrl + '/articles/:id/similar' + } }); }]); app.factory('TopicFactory', ['$resource', function($resource) { return $resource(Vipra.config.restUrl + '/topics/:id', {}, { - update: { method: 'PUT' }, - articles: { isArray: true, url: Vipra.config.restUrl + '/topics/:id/articles' } + update: { + method: 'PUT' + }, + articles: { + isArray: true, + url: Vipra.config.restUrl + '/topics/:id/articles' + } }); }]); @@ -38,4 +46,4 @@ return $resource(Vipra.config.restUrl + '/topicmodels/:id'); }]); -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/js/helpers.js b/vipra-ui/app/js/helpers.js index 5970ba06..267d5d42 100644 --- a/vipra-ui/app/js/helpers.js +++ b/vipra-ui/app/js/helpers.js @@ -96,4 +96,4 @@ log: function() {} }; -})(); +})(); \ No newline at end of file diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less index ed9d71d3..cb1d41d3 100644 --- a/vipra-ui/app/less/app.less +++ b/vipra-ui/app/less/app.less @@ -204,7 +204,7 @@ a:hover { .colorbox { display: inline-block; width: 10px; - height: 20px; + height: 21px; vertical-align: middle; float: right; border-radius: 3px; @@ -215,8 +215,8 @@ a:hover { display: inline-block; position: absolute; top: 0; - left: 0; - height: 100%; + right: 0; + height: 21px; width: 0px; background: @valuebg; transition: width .5s linear; diff --git a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java index 4355d78a..a69bd3ad 100644 --- a/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java +++ b/vipra-util/src/main/java/de/vipra/util/model/TopicWord.java @@ -55,12 +55,7 @@ public class TopicWord implements Comparable<TopicWord>, Serializable { @Override public int compareTo(final TopicWord o) { - final double l = likeliness - o.getLikeliness(); - if (l > 0) - return 1; - if (l < 0) - return -1; - return 0; + return Double.compare(likeliness, o.getLikeliness()); } @Override -- GitLab