Skip to content

Commit

Permalink
Merge pull request #4 from NLeSC/categoricalcolors
Browse files Browse the repository at this point in the history
Categorical colors
  • Loading branch information
Dafne van Kuppevelt authored May 24, 2017
2 parents 23c869f + 3dab4cc commit 171940e
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 17 deletions.
11 changes: 11 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
h4 {
display: block;
-webkit-margin-before: 1em;
-webkit-margin-after: 1em;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
font-weight: bold;
}

.App {
text-align: center;
background: #fff;
Expand All @@ -8,6 +17,8 @@
min-height: 100%;
min-width: 700px;
clear: both;
font-family: sans-serif;
font-size: 0.8em;
}

.App-logo {
Expand Down
5 changes: 3 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class App extends React.Component {
activeNode: {},
filterState: {
sizeAttributeValue: "degree",
colorAttributeValue: "degree",
colorAttributeValue: "community",
adjustLayout: false,
filterSelected: false
},
Expand Down Expand Up @@ -89,8 +89,9 @@ class App extends React.Component {
filterState.inDegreeValue = filterState.inDegreeValue || 0;
filterState.subjectValue = filterState.subjectValue || "all";
filterState.creatorValue = filterState.creatorValue || "all";
filterState.communityValue = filterState.communityValue || "all";
filterState.sizeAttributeValue = filterState.sizeAttributeValue || "degree";
filterState.colorAttributeValue = filterState.sizeAttributeValue || "color";
filterState.colorAttributeValue = filterState.colorAttributeValue || "creator";
return {
filterState: filterState
};
Expand Down
2 changes: 2 additions & 0 deletions src/components/AttributesPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class AttributesPane extends React.Component {
id,
date,
articles_s,
community,
abstract
} = this.props.activeNode;
if (this.props.activeNode.id) {
Expand All @@ -27,6 +28,7 @@ class AttributesPane extends React.Component {
<h4><a href={id} target="_blank">{title}</a> </h4>
<div> <b>Date: </b>{date} </div>
<div> <b>Articles: </b> {articles_s} </div>
<div> <b>Community: </b> {community} </div>
<div> <b>Abstract: </b>{abstract}</div>
</div>
);
Expand Down
69 changes: 61 additions & 8 deletions src/components/ColorOnAttribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,36 @@ class ColorOnAttribute extends React.Component {

constructor(props: Props) {
super(props);
this.updateSize(props);
this.updateColor(props);
this.updateColorCategorical = this.updateColorCategorical.bind(this);
this.updateColorNumerical = this.updateColorNumerical.bind(this);
}

componentWillReceiveProps(props: Props) {
this.updateSize(props);
this.updateColor(props);
}

updateSize(props) {
updateColor(props) {
const s = props.sigma;
const att = props.attribute;
const nodes = s.graph.nodes();
if (nodes.length > 0) {
if (typeof nodes[0][att] === 'string') {
this.updateColorCategorical(nodes, att);
} else {
this.updateColorNumerical(nodes, att);
}

}

}

updateColorNumerical(nodes, att) {
let minValue = Number.MAX_SAFE_INTEGER;
let maxValue = 0;
s.graph.nodes().forEach(node => {
minValue = Math.min(minValue, node[props.attribute]);
maxValue = Math.max(maxValue, node[props.attribute]);
nodes.forEach(node => {
minValue = Math.min(minValue, node[att]);
maxValue = Math.max(maxValue, node[att]);
});

//update color
Expand All @@ -49,8 +65,8 @@ class ColorOnAttribute extends React.Component {
format: 'hex', // "hex" or "rgb" or "rgbaString"
alpha: 1 // set an alpha value or a linear alpha mapping [start, end]
});
s.graph.nodes().forEach(node => {
const index = 2 + (node[props.attribute] - minValue) * scale;
nodes.forEach(node => {
const index = 2 + (node[att] - minValue) * scale;
node.color_selected = cm_selected[Math.floor(index)];
node.color_unselected = cm_unselected[Math.floor(index)];
if (node.selected) {
Expand All @@ -59,7 +75,44 @@ class ColorOnAttribute extends React.Component {
node.color = node.color_unselected;
}
});
}

updateColorCategorical(nodes, att) {
let values = [];
nodes.forEach(node => {
let value = node[att];
if (values.indexOf(value) === -1) {
values.push(value);
}
});

//update color
const nshades = Math.max(11, values.length);
const scale = nshades / values.length;
const cm_unselected = colormap({
colormap: 'jet', // pick a builtin colormap or add your own
nshades: nshades, // how many divisions
format: 'rgbaString', // "hex" or "rgb" or "rgbaString"
alpha: 0.5 // set an alpha value or a linear alpha mapping [start, end]
});

const cm_selected = colormap({
colormap: 'jet', // pick a builtin colormap or add your own
nshades: nshades, // how many divisions
format: 'rgbaString', // "hex" or "rgb" or "rgbaString"
alpha: 1 // set an alpha value or a linear alpha mapping [start, end]
});

nodes.forEach(node => {
const index = Math.floor(scale * values.indexOf(node[att]));
node.color_selected = cm_selected[index]; //'black';
node.color_unselected = cm_unselected[index];
if (node.selected) {
node.color = node.color_selected;
} else {
node.color = node.color_unselected;
}
});
}


Expand Down
29 changes: 28 additions & 1 deletion src/components/FilterPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class FilterPane extends Component {
this.handleInDegreeChange = this.handleInDegreeChange.bind(this);
this.handleSubjectChange = this.handleSubjectChange.bind(this);
this.handleCreatorChange = this.handleCreatorChange.bind(this);
this.handleCommunityChange = this.handleCommunityChange.bind(this);
this.handleMinYearChange = this.handleMinYearChange.bind(this);
this.handleMaxYearChange = this.handleMaxYearChange.bind(this);
this.handleSizeAttributeChange = this.handleSizeAttributeChange.bind(this);
Expand Down Expand Up @@ -62,6 +63,13 @@ class FilterPane extends Component {
this.props.onChange(newState);
}

handleCommunityChange(event) {
const newState = {
communityValue: event.target.value
};
this.props.onChange(newState);
}

handleSizeAttributeChange(event) {
const newState = {
sizeAttributeValue: event.target.value
Expand Down Expand Up @@ -101,6 +109,7 @@ class FilterPane extends Component {
const maxYear = graphProps.maxYear || 2016;
const subjectCategories = graphProps.subjectCategories || {};
const creatorCategories = graphProps.creatorCategories || {};
const communityCategories = graphProps.communityCategories || {};
const listSubjectOptions = Object.keys(subjectCategories).map(
(option) => <option value={option} key={option}> {subjectCategories[option]} </option>
);
Expand All @@ -109,23 +118,40 @@ class FilterPane extends Component {
(option) => <option value={option} key={option}> {creatorCategories[option]} </option>
);
const sizeAttributes = graphProps.sizeAttributes || [];
const colorAttributes = graphProps.colorAttributes || [];
const listSizeAttributes = sizeAttributes.map(
(option) => <option value={option} key={option}> {option} </option>
);
const listColorAttributes = sizeAttributes.map(
const listColorAttributes = colorAttributes.map(
(option) => <option value={option} key={option}> {option} </option>
);

// The current values
const inDegreeValue = this.props.filterState.inDegreeValue || minInDegree;
const subjectValue = this.props.filterState.subjectValue;
const creatorValue = this.props.filterState.creatorValue;
const communityValue = this.props.filterState.communityValue;
const minYearValue = this.props.filterState.minYearValue || minYear;
const maxYearValue = this.props.filterState.maxYearValue || maxYear;
const sizeAttributeValue = this.props.filterState.sizeAttributeValue;
const colorAttributeValue = this.props.filterState.colorAttributeValue;
const adjustLayout = this.props.filterState.adjustLayout;
const filterSelected = this.props.filterState.filterSelected; //|| false;

let communityFilter = null;
if (Object.keys(communityCategories).length > 0) {
const listCommunityOptions = Object.keys(communityCategories).map(
(option) => <option value={option} key={option}> {communityCategories[option]} </option>
);
communityFilter = <div>
<h4>Community</h4>
<select value={communityValue} onChange={this.handleCommunityChange}>
<option value="all">All</option>
{listCommunityOptions}
</select>
</div>
}

return (
<div>
<h2>Filters</h2>
Expand Down Expand Up @@ -161,6 +187,7 @@ class FilterPane extends Component {
{listCreatorOptions}
</select>
</div>
{communityFilter}
<div>
<h4>Selection </h4>
<label> Filter selected:
Expand Down
23 changes: 22 additions & 1 deletion src/components/GraphProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class GraphProperties extends React.Component {
constructor(props: Props) {
super(props);
this.getGraphProperties = this.getGraphProperties.bind(this);
this.getcolorAttributes = this.getcolorAttributes.bind(this);
}


Expand All @@ -27,6 +28,7 @@ class GraphProperties extends React.Component {
maxYear = 0;
const subjectCategories = {};
const creatorCategories = {};
const communityCategories = {};
s.graph.nodes().forEach(node => {
const inDegree = s.graph.degree(node.id, "in");
maxInDegree = Math.max(maxInDegree, inDegree);
Expand All @@ -40,6 +42,9 @@ class GraphProperties extends React.Component {
subjectCategories[node.subject] = subj_split[subj_split.length - 1];
const creator_split = node.creator.split("/");
creatorCategories[node.creator] = creator_split[creator_split.length - 1];
if (node.community) {
communityCategories[node.community] = node.community;
}
});
return {
minInDegree: 0,
Expand All @@ -48,7 +53,9 @@ class GraphProperties extends React.Component {
maxYear: maxYear,
subjectCategories: subjectCategories,
creatorCategories: creatorCategories,
sizeAttributes: this.getSizeAttributes()
communityCategories: communityCategories,
sizeAttributes: this.getSizeAttributes(),
colorAttributes: this.getcolorAttributes()
};
}

Expand All @@ -66,6 +73,20 @@ class GraphProperties extends React.Component {
return filteredAttributes;
}

getcolorAttributes() {
const attributes = ['creator', 'subject', 'community', 'degree', 'in_degree', 'out_degree', 'year', 'hubs', 'authorities', 'betweenness_centrality', 'closeness_centrality', 'count_annotation', 'rel_in_degree', 'pagerank'];
// Check if first node contains these attributes
const s = this.props.sigma;
let exampleNode = [];
if (s.graph.nodes().length > 0) {
exampleNode = s.graph.nodes()[0];
}
const filteredAttributes = attributes.filter(att => {
return (att in exampleNode);
});
return filteredAttributes;
}

render() {
return (
<div>{ embedProps(this.props.children, {sigma: this.props.sigma}) }</div>
Expand Down
5 changes: 3 additions & 2 deletions src/components/Network.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Network extends React.Component {
handleChange(e) {
const node = e.data.node;
console.log(node);

if (node.selected) {
node.selected = false;
} else {
Expand All @@ -46,7 +47,7 @@ class Network extends React.Component {

onGraphLoaded(data) {
data.nodes.forEach(node => {
node.articles_s = node.articles.join(", ");
node.articles_s = node.articles ? node.articles.join(", ") : "";
node.title = node.title === "" ? node.ecli : node.title;
let ecli_split = node.ecli.split(":");
node.label = ecli_split[2] + ' ' + ecli_split[3];
Expand Down Expand Up @@ -78,7 +79,7 @@ class Network extends React.Component {
<SigmaNetwork renderer="canvas" style={{maxWidth:"inherit", height:"700px"}}
graph={data}
settings={{minNodeSize:3, minArrowSize:4, defaultEdgeColor:'#000'}}
onClickNode={this.handleChange}
onClickNode={this.handleChange}
updateFilterProps={this.updateFilterProps}
filterState={this.props.filterState}
loading={false}
Expand Down
22 changes: 20 additions & 2 deletions src/components/SigmaNetwork.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import EmptyComponent from './utils.js';
class SigmaNetwork extends Sigma {

constructor(props) {

super(props);
this.getVisibleNodes = this.getVisibleNodes.bind(this);
}


getVisibleNodes() {
console.log("Retrieving visible nodes");
const visibleNodes = [];
Expand All @@ -33,6 +35,8 @@ class SigmaNetwork extends Sigma {
}




componentDidMount() {
this.props.setVisibleNodeFunction(this.getVisibleNodes);
}
Expand Down Expand Up @@ -70,6 +74,16 @@ class SigmaNetwork extends Sigma {
};
};

const nodesByCommunity = community => {
return node => {
if (community === "all") {
return true;
} else {
return node.community === community;
}
};
};

const nodesBySelected = filterSelected => {
return node => {
if (filterSelected) {
Expand All @@ -81,18 +95,22 @@ class SigmaNetwork extends Sigma {
};


const nodesBy = (indegree, subject, creator, minYearValue, maxYearValue, filterSelected) => {
const nodesBy = (indegree, subject, creator, community, minYearValue, maxYearValue, filterSelected) => {
return node => nodesByIndegree(indegree)(node) &&
nodesByYear(minYearValue, maxYearValue)(node) &&
nodesBySubject(subject)(node) &&
nodesByCreator(creator)(node) &&
nodesByCommunity(community)(node) &&
nodesBySelected(filterSelected)(node);
};



const {
inDegreeValue,
subjectValue,
creatorValue,
communityValue,
minYearValue,
maxYearValue,
sizeAttributeValue,
Expand Down Expand Up @@ -122,7 +140,7 @@ class SigmaNetwork extends Sigma {
<GraphProperties onInitialization={this.props.updateFilterProps} sigma={this.sigma}>
<SizeOnAttribute attribute={sizeAttributeValue}>
<ColorOnAttribute attribute={colorAttributeValue}>
<Filter nodesBy={nodesBy(inDegreeValue, subjectValue, creatorValue, minYearValue, maxYearValue, filterSelected)}/>
<Filter nodesBy={nodesBy(inDegreeValue, subjectValue, creatorValue, communityValue, minYearValue, maxYearValue, filterSelected)}/>
{forceLayout}
</ColorOnAttribute>
</SizeOnAttribute>
Expand Down
2 changes: 1 addition & 1 deletion src/data/data.json

Large diffs are not rendered by default.

0 comments on commit 171940e

Please sign in to comment.