apoc.gephi.Gephi Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apoc Show documentation
Show all versions of apoc Show documentation
A collection of useful Neo4j Procedures
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package apoc.gephi;
import static apoc.util.MapUtil.map;
import apoc.Extended;
import apoc.graph.Graphs;
import apoc.result.ProgressInfo;
import apoc.util.JsonUtil;
import apoc.util.UrlResolver;
import apoc.util.Util;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.neo4j.graphdb.Entity;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
/**
* @author mh
* @since 29.05.16
*/
// https://github.com/gephi/gephi/wiki/GraphStreaming#Gephi_as_Master
// https://marketplace.gephi.org/plugin/graph-streaming/
@Extended
public class Gephi {
public static final int WIDTH = 1000;
public static final int HEIGHT = 1000;
private String getGephiUrl(String hostOrKey) {
return new UrlResolver("http", "localhost", 8080).getUrl("gephi", hostOrKey);
}
public static double doubleValue(Entity pc, String prop, Number defaultValue) {
return Util.toDouble(pc.getProperty(prop, defaultValue));
}
private static final String[] CAPTIONS = new String[] {"name", "title", "label"};
private static final List RESERVED =
Arrays.asList("label", "TYPE", "id", "source", "target", "weight", "directed");
// http://127.0.0.1:8080/workspace0?operation=updateGraph
// TODO configure property-filters or transfer all properties
@Procedure
@Description(
"apoc.gephi.add(url-or-key, workspace, data, weightproperty, ['exportproperty']) | streams passed in data to Gephi")
public Stream add(
@Name("urlOrKey") String keyOrUrl,
@Name("workspace") String workspace,
@Name("data") Object data,
@Name(value = "weightproperty", defaultValue = "null") String weightproperty,
@Name(value = "exportproperties", defaultValue = "[]") List exportproperties) {
if (workspace == null) workspace = "workspace0";
String url = getGephiUrl(keyOrUrl) + "/" + Util.encodeUrlComponent(workspace) + "?operation=updateGraph";
long start = System.currentTimeMillis();
HashSet nodes = new HashSet<>(1000);
HashSet rels = new HashSet<>(10000);
List propertyNames = new ArrayList<>(exportproperties);
propertyNames.removeAll(RESERVED);
if (Graphs.extract(data, nodes, rels)) {
String payload = toGephiStreaming(
nodes, rels, weightproperty, propertyNames.toArray(new String[propertyNames.size()]));
JsonUtil.loadJson(url, map("method", "POST", "Content-Type", "application/json; charset=utf-8"), payload)
.count();
return Stream.of(new ProgressInfo(url, "graph", "gephi")
.update(nodes.size(), rels.size(), nodes.size())
.done(start));
}
return Stream.empty();
}
private String toGephiStreaming(
Collection nodes, Collection rels, String weightproperty, String[] exportproperties) {
return Stream.concat(
toGraphStream(nodes, "an", weightproperty, exportproperties),
toGraphStream(rels, "ae", weightproperty, exportproperties))
.collect(Collectors.joining("\r\n"));
}
private Stream toGraphStream(
Collection extends Entity> source, String operation, String weightproperty, String[] exportproperties) {
Map> colors = new HashMap<>();
return source.stream()
.map(n -> map(operation, data(n, colors, weightproperty, exportproperties)))
.map(Util::toJson);
}
private Map data(
Entity pc, Map> colors, String weightproperty, String[] exportproperties) {
if (pc instanceof Node) {
Node n = (Node) pc;
String labels = Util.labelString(n);
Map attributes = map("label", caption(n), "TYPE", labels);
attributes.putAll(positions());
attributes.putAll(color(labels, colors));
if (exportproperties.length > 0) attributes.putAll(n.getProperties(exportproperties));
return map(idStr(n), attributes);
}
if (pc instanceof Relationship) {
Relationship r = (Relationship) pc;
String type = r.getType().name();
Map attributes = map("label", type, "TYPE", type);
Double weight = Util.doubleValue(r, weightproperty, 1.0);
attributes.putAll(map(
"source",
idStr(r.getStartNode()),
"target",
idStr(r.getEndNode()),
"directed",
true,
"weight",
weight));
attributes.putAll(color(type, colors));
if (exportproperties.length > 0) attributes.putAll(r.getProperties(exportproperties));
return map(String.valueOf(r.getId()), attributes);
}
return map();
}
private Map positions() {
return map("size", 10, "x", WIDTH / 2 - Math.random() * WIDTH, "y", HEIGHT / 2 - Math.random() * HEIGHT);
}
private Map color(String type, Map> colors) {
return colors.computeIfAbsent(type, k -> {
int rgb = type.hashCode();
float r = (float) (rgb >> 16 & 0xFF) / 255.0f;
float g = (float) (rgb >> 8 & 0xFF) / 255.0f;
float b = (float) (rgb & 0xFF) / 255.0f;
return map("r", r, "g", g, "b", b);
});
}
private String idStr(Node n) {
return String.valueOf(n.getId());
}
private String caption(Node n) {
for (String caption : CAPTIONS) {
if (n.hasProperty(caption)) return n.getProperty(caption).toString();
}
String first = null;
for (String caption : CAPTIONS) {
for (String key : n.getPropertyKeys()) {
if (first == null) first = key;
if (key.toLowerCase().contains(caption))
return n.getProperty(key).toString();
}
}
return first == null ? idStr(n) : n.getProperty(first).toString();
}
}