4 | Website : http://www.gephi.org
5 |
6 | This file is part of Gephi.
7 |
8 | Gephi is free software: you can redistribute it and/or modify
9 | it under the terms of the GNU Affero General Public License as
10 | published by the Free Software Foundation, either version 3 of the
11 | License, or (at your option) any later version.
12 |
13 | Gephi is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 | GNU Affero General Public License for more details.
17 |
18 | You should have received a copy of the GNU Affero General Public License
19 | along with Gephi. If not, see .
20 | */
21 | package utils
22 |
23 | import java.awt.Color
24 | import java.io.File
25 | import java.io.IOException
26 | import java.awt.BorderLayout
27 | import javax.swing.JFrame
28 | import org.gephi.data.attributes.api.AttributeColumn
29 | import org.gephi.data.attributes.api.AttributeController
30 | import org.gephi.data.attributes.api.AttributeModel
31 | import org.gephi.filters.api.FilterController
32 | import org.gephi.filters.api.Query
33 | import org.gephi.filters.api.Range
34 | import org.gephi.filters.plugin.graph.DegreeRangeBuilder.DegreeRangeFilter
35 | import org.gephi.graph.api._
36 | import org.gephi.io.exporter.api.ExportController
37 | import org.gephi.io.importer.api.Container
38 | import org.gephi.io.importer.api.EdgeDefault
39 | import org.gephi.io.importer.api.ImportController
40 | import org.gephi.io.processor.plugin.DefaultProcessor
41 | import org.gephi.layout.plugin.force.StepDisplacement
42 | import org.gephi.layout.plugin.forceAtlas.ForceAtlasLayout
43 | import org.gephi.preview.types.EdgeColor
44 | import org.gephi.project.api.ProjectController
45 | import org.gephi.project.api.Workspace
46 | import org.gephi.ranking.api.Ranking
47 | import org.gephi.ranking.api.RankingController
48 | import org.gephi.ranking.api.Transformer
49 | import org.gephi.ranking.plugin.transformer.AbstractColorTransformer
50 | import org.gephi.ranking.plugin.transformer.AbstractSizeTransformer
51 | import org.gephi.statistics.plugin.GraphDistance
52 | import org.gephi.statistics.plugin.Modularity
53 | import org.openide.util.Lookup
54 | import org.gephi.io.exporter.preview._
55 | import org.gephi.io.importer.api.Container
56 | import org.gephi.io.importer.api.ImportController
57 | import org.gephi.io.processor.plugin.DefaultProcessor
58 | import org.gephi.preview.api._
59 | import org.gephi.preview.types.DependantOriginalColor
60 | import processing.core.PApplet
61 |
62 | import scala.collection.{ mutable, immutable, generic }
63 | import scala.collection.mutable.{ HashMap }
64 | import scala.util.Random
65 |
66 | import renren.Friend
67 | import utils.Reporters._
68 |
69 | /**
70 | * This demo shows several actions done with the toolkit, aiming to do a complete chain,
71 | * from data import to results.
72 | *
73 | * This demo shows the following steps:
74 | *
- Create a project and a workspace, it is mandatory.
75 | * - Import the
polblogs.gml
graph file in an import container.
76 | * - Append the container to the main graph structure.
77 | * - Filter the graph, using
DegreeFilter
.
78 | * - Run layout manually.
79 | * - Compute graph distance metrics.
80 | * - Rank color by degree values.
81 | * - Rank size by centrality values.
82 | * - Configure preview to display labels and mutual edges differently.
83 | * - Export graph as PDF.
84 | *
85 | * @author Mathieu Bastian
86 | */
87 | class Grapher(val network: HashMap[Friend, List[Friend]], reporter: Reporter) {
88 | val minSize = 10
89 | val maxSize = 50
90 | val height = 1080
91 | val width = 1920
92 | val colorMap: HashMap[Integer, Color] = new HashMap()
93 |
94 | def generateRandomColor(mix: Color): Color = {
95 | var random = new Random()
96 | var red = random.nextInt(256)
97 | var green = random.nextInt(256)
98 | var blue = random.nextInt(256)
99 |
100 | // mix the color
101 | red = (red + mix.getRed()) / 2
102 | green = (green + mix.getGreen()) / 2
103 | blue = (blue + mix.getBlue()) / 2
104 |
105 | var color = new Color(red, green, blue)
106 | return color
107 | }
108 |
109 | def fillMap() = {
110 | var mix = new Color(0, 0, 0)
111 | for (i <- 0 to 100) {
112 | colorMap(i) = generateRandomColor(mix)
113 | mix = colorMap(i)
114 | }
115 | }
116 |
117 | fillMap()
118 |
119 | def rgbInt2Float(a: Integer): Float = {
120 | return a.toFloat / 256f
121 | }
122 |
123 | def script(): Unit = {
124 | reporter.info("Begin to draw")
125 | //Init a project - and therefore a workspace
126 | var pc = Lookup.getDefault().lookup(classOf[ProjectController])
127 | pc.newProject()
128 | var workspace = pc.getCurrentWorkspace()
129 |
130 | //Get models and controllers for this new workspace - will be useful later
131 | var attributeModel = Lookup.getDefault().lookup(classOf[AttributeController]).getModel()
132 | var graphModel = Lookup.getDefault().lookup(classOf[GraphController]).getModel()
133 | var previewController = Lookup.getDefault().lookup(classOf[PreviewController])
134 | var model = previewController.getModel()
135 | var filterController = Lookup.getDefault().lookup(classOf[FilterController])
136 | var rankingController = Lookup.getDefault().lookup(classOf[RankingController])
137 |
138 | // Import the data
139 | var directedGraph = graphModel.getDirectedGraph()
140 | var nodes: HashMap[Friend, Node] = new HashMap()
141 | // println(network)
142 | for ((k, _) <- network) {
143 | var n0 = graphModel.factory().newNode(k.uid)
144 | n0.getNodeData().setLabel(k.name)
145 | nodes(k) = n0
146 | directedGraph.addNode(n0)
147 | }
148 | for ((k, value) <- network) {
149 | for (ele <- value) {
150 | var e0 = graphModel.factory().newEdge(nodes(k), nodes(ele), 1f, true)
151 | directedGraph.addEdge(e0)
152 | }
153 | }
154 |
155 | // System.out.println("Nodes: "+directedGraph.getNodeCount()+" Edges: "+directedGraph.getEdgeCount())
156 |
157 | //Filter
158 | var degreeFilter = new DegreeRangeFilter()
159 | degreeFilter.init(directedGraph)
160 | degreeFilter.setRange(new Range(1, Integer.MAX_VALUE)) //Remove nodes with degree < 30
161 | var query = filterController.createQuery(degreeFilter)
162 | var view = filterController.filter(query)
163 | graphModel.setVisibleView(view) //Set the filter result as the visible view
164 |
165 | //See visible graph stats
166 | var graphVisible = graphModel.getUndirectedGraphVisible()
167 | reporter.info("---The Graph Info---")
168 | reporter.info("Nodes: " + graphVisible.getNodeCount())
169 | reporter.info("Edges: " + graphVisible.getEdgeCount())
170 |
171 | //Get Centrality
172 | var distance = new GraphDistance()
173 | distance.setDirected(true)
174 | distance.execute(graphModel, attributeModel)
175 |
176 | //Rank color by Degree
177 | // var degreeRanking = rankingController.getModel().getRanking(Ranking.NODE_ELEMENT, Ranking.DEGREE_RANKING)
178 | // var colorTransformer = rankingController.getModel().getTransformer(Ranking.NODE_ELEMENT, Transformer.RENDERABLE_COLOR).asInstanceOf[AbstractColorTransformer[Ranking[AttributeColumn]]]
179 | // colorTransformer.setColors(Array(new Color(0xFEF0D9), new Color(0xB30000)))
180 | // rankingController.transform(degreeRanking,colorTransformer)
181 |
182 | //Rank size by centrality
183 | var centralityColumn = attributeModel.getNodeTable().getColumn(GraphDistance.BETWEENNESS)
184 | var centralityRanking = rankingController.getModel().getRanking(Ranking.NODE_ELEMENT, centralityColumn.getId)
185 | var sizeTransformer = rankingController.getModel().getTransformer(Ranking.NODE_ELEMENT, Transformer.RENDERABLE_SIZE).asInstanceOf[AbstractSizeTransformer[Ranking[AttributeColumn]]]
186 | sizeTransformer.setMinSize(minSize)
187 | sizeTransformer.setMaxSize(maxSize)
188 | rankingController.transform(centralityRanking, sizeTransformer)
189 |
190 | // Rank color by modularity
191 | var modularity = new Modularity
192 | modularity.execute(graphModel, attributeModel)
193 | var modularityColumn = attributeModel.getNodeTable().getColumn(Modularity.MODULARITY_CLASS)
194 | for (node <- graphModel.getDirectedGraph.getNodes.toArray) {
195 | var rgb = node.getNodeData().getAttributes().getValue(modularityColumn.getIndex()).asInstanceOf[Int]
196 | var colorBuf = colorMap(rgb)
197 | node.getNodeData().setColor(rgbInt2Float(colorBuf.getRed), rgbInt2Float(colorBuf.getGreen), rgbInt2Float(colorBuf.getBlue))
198 | }
199 |
200 | // use layout
201 | reporter.info("Run layout algorithm")
202 | val layout = new ForceAtlasLayout(null)
203 | layout.setGraphModel(graphModel)
204 | layout.resetPropertiesValues()
205 | layout.initAlgo()
206 |
207 | var i = 0
208 | while (i < 1000 && layout.canAlgo()) {
209 | i = i + 1
210 | layout.goAlgo()
211 | }
212 | layout.endAlgo()
213 | reporter.info("Layout end")
214 |
215 | // Test: get all the AttributeColumn
216 | // for (col <- attributeModel.getNodeTable().getColumns()) {
217 | // println(col)
218 | // }
219 |
220 | //Preview
221 | model.getProperties().putValue(PreviewProperty.SHOW_NODE_LABELS, true)
222 | model.getProperties().putValue(PreviewProperty.NODE_LABEL_FONT, model.getProperties().getFontValue(PreviewProperty.NODE_LABEL_FONT).deriveFont(8))
223 | model.getProperties().putValue(PreviewProperty.EDGE_CURVED, false)
224 | previewController.refreshPreview()
225 |
226 | //New Processing target, get the PApplet
227 | var target = previewController.getRenderTarget(RenderTarget.PROCESSING_TARGET).asInstanceOf[ProcessingTarget]
228 | var applet = target.getApplet()
229 | applet.init()
230 |
231 | //Refresh the preview and reset the zoom
232 | previewController.render(target)
233 | target.refresh()
234 | target.resetZoom()
235 |
236 | //Add the applet to a JFrame and display
237 | reporter.info("Build JFrame")
238 | var frame = new JFrame("Renren Friend Relationship")
239 | frame.setLayout(new BorderLayout())
240 |
241 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
242 | frame.add(applet, BorderLayout.CENTER)
243 |
244 | frame.pack()
245 | frame.setVisible(true)
246 | var ec = Lookup.getDefault().lookup(classOf[ExportController])
247 | val pe = new PNGExporter
248 | ec.exportFile(new File("headless_simple.png"), pe)
249 | val ps = new SVGExporter
250 | ec.exportFile(new File("headless_simple.svg"), ps)
251 |
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/main/scala/utils/httpparser.scala:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | // jsoup
4 | import org.jsoup.Jsoup;
5 | import org.jsoup.helper.Validate;
6 | import org.jsoup.nodes.Document;
7 | import org.jsoup.nodes.Element;
8 | import org.jsoup.select.Elements;
9 |
10 | // project utils
11 | import utils.Reporters._
12 |
13 | // utilities
14 | import scala.collection.{ mutable, immutable, generic }
15 |
16 | class HttpParser(username: String, reporter: Reporter, dumper: Dumper) {
17 | def getUid(content: String): String = {
18 | var document = Jsoup.parse(content)
19 | var linkList = document.select("a[href~=renren.com/[0-9]+/profile]")
20 |
21 | // can't use for or map to iterate the Elements
22 | var href: java.util.Iterator[Element] = linkList.iterator()
23 | var objectName = href.next()
24 | var hrefValue = objectName.attr("href");
25 | val uid = hrefValue.split("/")(3)
26 | reporter.info(username + "'s uid is " + uid)
27 | uid
28 | }
29 |
30 | def getFriendCount(content: String): Int = {
31 | var document = Jsoup.parse(content)
32 | var linkList = document.select("span[class=count]")
33 |
34 | // can't use for or map to iterate the Elements
35 | val href: java.util.Iterator[Element] = linkList.iterator()
36 | var objectName = href.next()
37 | var count = objectName.html
38 | reporter.info(" has " + count.toString + " friends")
39 | count.toInt
40 | }
41 |
42 | def getFriendsInOnePage(content: String): List[Element] = {
43 | var document = Jsoup.parse(content)
44 | var linkList = document.select("ol[id=friendListCon]")
45 | if (linkList.size != 0) {
46 | var elements = linkList.iterator().next().children()
47 | var list: List[Element] = List()
48 | var it: java.util.Iterator[Element] = elements.iterator()
49 | while (it.hasNext()) {
50 | var obj = it.next()
51 | list = obj :: list
52 | }
53 | list
54 | } else {
55 | Nil
56 | }
57 | }
58 |
59 | def getSchoolRank(friends: List[Element]) = {
60 | var hmp = new scala.collection.mutable.ListMap[String, Int]()
61 | for (friend <- friends) {
62 | var infos = friend.select("dt")
63 | var it: java.util.Iterator[Element] = infos.iterator()
64 |
65 | // remove name
66 | it.next()
67 |
68 | var schoolInfo = it.next().html()
69 | if (schoolInfo.toString == "学校") {
70 | infos = friend.select("dd")
71 | it = infos.iterator()
72 |
73 | it.next()
74 |
75 | schoolInfo = it.next().html()
76 | hmp(schoolInfo) = (hmp getOrElse (schoolInfo, 0)) + 1
77 | }
78 | }
79 |
80 | val list = hmp.toList.sortWith(_._2 > _._2)
81 | dumper.writeSchoolRank(list)
82 | }
83 | }
--------------------------------------------------------------------------------
/src/main/scala/utils/reporter.scala:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import scala.tools.nsc.Global
4 | import scala.reflect.internal.util.{ Position, NoPosition, FakePos }
5 |
6 | object Reporters {
7 |
8 | abstract class ReporterFormatter {
9 | def formatTypeTitle(typ: MsgType): String
10 | }
11 |
12 | class ConsoleFormatter extends ReporterFormatter {
13 | def formatTypeTitle(typ: MsgType) = {
14 | typ match {
15 | case FatalMsg =>
16 | Console.RED + Console.BOLD + typ.title + Console.RESET
17 | case ErrorMsg =>
18 | Console.RED + typ.title + Console.RESET
19 | case WarningMsg =>
20 | Console.YELLOW + typ.title + Console.RESET
21 | case NormalMsg =>
22 | Console.MAGENTA + typ.title + Console.RESET
23 | case TitleMsg =>
24 | Console.BLUE + Console.BOLD + typ.title + Console.RESET
25 | case DebugMsg =>
26 | typ.title
27 | }
28 | }
29 | }
30 |
31 | class PlainFormatter extends ReporterFormatter {
32 | def formatTypeTitle(typ: MsgType) = {
33 | typ.title
34 | }
35 | }
36 |
37 | sealed abstract class MsgType {
38 | val title: String
39 | }
40 |
41 | object MsgType {
42 | val maxTitleSize = WarningMsg.title.size
43 | }
44 |
45 | case object TitleMsg extends MsgType {
46 | val title = "info"
47 | }
48 |
49 | case object FatalMsg extends MsgType {
50 | val title = "fatal"
51 | }
52 |
53 | case object ErrorMsg extends MsgType {
54 | val title = "error"
55 | }
56 |
57 | case object NormalMsg extends MsgType {
58 | val title = "info"
59 | }
60 |
61 | case object WarningMsg extends MsgType {
62 | val title = "warning"
63 | }
64 |
65 | case object DebugMsg extends MsgType {
66 | val title = "debug"
67 | }
68 |
69 | final case class MsgLines(lines: Seq[String]);
70 |
71 | case class Msg(lines: Seq[String], typ: MsgType) {
72 | def content = lines.mkString("\n")
73 |
74 | val firstLine = lines.head
75 | val otherLines = lines.tail
76 | }
77 |
78 | import language.implicitConversions
79 |
80 | implicit def posToOptPos(p: Position): Option[Position] = Some(p)
81 | implicit def strToMsgLines(m: String): MsgLines = MsgLines(Seq(m))
82 | implicit def seqStrToMsgLines(m: Seq[String]): MsgLines = MsgLines(m)
83 |
84 | trait ReporterHandler {
85 | def open() {}
86 | def incIndent() {}
87 | def decIndent() {}
88 | def close() {}
89 |
90 | def printMessage(msg: Msg, optPos: Option[Position])
91 | def printText(content: String)
92 | }
93 |
94 | class ConsoleReporterHandler() extends ReporterHandler {
95 | var currentIndent: Int = 0;
96 | val indentStep = 8;
97 |
98 | val formatter = new ConsoleFormatter
99 |
100 | protected def posToString(optPos: Option[Position]): String = {
101 | optPos match {
102 | case Some(posIn) =>
103 | val pos = if (posIn eq null) NoPosition
104 | else if (posIn.isDefined) posIn.inUltimateSource(posIn.source)
105 | else posIn
106 |
107 | pos match {
108 | case FakePos(fmsg) =>
109 | "?:? (" + fmsg + "): "
110 | case NoPosition =>
111 | ""
112 |
113 | case _ =>
114 | val file = pos.source.file
115 |
116 | file.path + ":" + pos.line + ": "
117 | }
118 |
119 | case None =>
120 | ""
121 | }
122 | }
123 |
124 | override def incIndent() {
125 | currentIndent += indentStep
126 | }
127 | override def decIndent() {
128 | currentIndent -= indentStep
129 | }
130 |
131 | override def printMessage(msg: Msg, optPos: Option[Position]) {
132 | val strPos = posToString(optPos)
133 |
134 | val indent = " " * currentIndent
135 | val padding = " " * (MsgType.maxTitleSize - msg.typ.title.size)
136 |
137 | printText(formatter.formatTypeTitle(msg.typ) + padding + ": " + indent + msg.firstLine + "\n")
138 | for (line <- msg.otherLines) {
139 | printText(" " * (MsgType.maxTitleSize + (": " + indent).length) + line + "\n")
140 | }
141 |
142 | optPos match {
143 | case Some(posIn) if posIn ne null =>
144 | val pos = if (posIn.isDefined) posIn.inUltimateSource(posIn.source)
145 | else posIn
146 |
147 | pos match {
148 | case FakePos(fmsg) =>
149 | case NoPosition =>
150 | case _ =>
151 | printSourceLine(strPos, pos)
152 | }
153 | case _ =>
154 | }
155 | }
156 |
157 | def printSourceLine(prefix: String, pos: Position) = {
158 | printText(prefix + pos.lineContent.stripLineEnd + "\n")
159 | if (pos.isDefined) {
160 | printText((" " * (pos.column - 1 + prefix.length) + "^\n"))
161 | }
162 | }
163 |
164 | def printText(content: String) {
165 | print(content)
166 | }
167 |
168 | }
169 |
170 | class Reporter() {
171 | var handlers = Set[ReporterHandler]()
172 |
173 | def dispatch(cb: ReporterHandler => Unit) {
174 | handlers.foreach(cb)
175 | }
176 |
177 | def attach(rh: ReporterHandler) {
178 | handlers += rh
179 | }
180 |
181 | def detach(rh: ReporterHandler) {
182 | handlers -= rh
183 | }
184 |
185 | def open() {
186 | dispatch(_.open)
187 | }
188 |
189 | def close() {
190 | dispatch(_.close)
191 | }
192 |
193 | def printMessage(m: Msg, optPos: Option[Position]) {
194 | dispatch { rh => rh.printMessage(m, optPos) }
195 | }
196 |
197 | def printText(s: String) {
198 | dispatch { rh => rh.printText(s) }
199 | }
200 |
201 | def incIndent() {
202 | dispatch(_.incIndent)
203 | }
204 |
205 | def decIndent() {
206 | dispatch(_.decIndent)
207 | }
208 |
209 | def msg(m: MsgLines, optPos: Option[Position] = None) =
210 | printMessage(Msg(m.lines, NormalMsg), optPos)
211 |
212 | def info(m: MsgLines, optPos: Option[Position] = None) =
213 | printMessage(Msg(m.lines, NormalMsg), optPos)
214 |
215 | def error(m: MsgLines, optPos: Option[Position] = None) =
216 | printMessage(Msg(m.lines, ErrorMsg), optPos)
217 |
218 | def fatal(m: MsgLines, optPos: Option[Position] = None) = {
219 | printMessage(Msg(m.lines, FatalMsg), optPos)
220 | sys.error("Panic! Evacuate Ship!")
221 | }
222 |
223 | def debug(m: MsgLines, optPos: Option[Position] = None) =
224 | printMessage(Msg(m.lines, DebugMsg), optPos)
225 |
226 | def warn(m: MsgLines, optPos: Option[Position] = None) =
227 | printMessage(Msg(m.lines, WarningMsg), optPos)
228 |
229 | def title(m: String) {
230 | printMessage(Msg(Seq(m), TitleMsg), None)
231 | }
232 | }
233 |
234 | case class CompilerReporterPassThrough(as: (String, Position) => Unit) extends scala.tools.nsc.reporters.Reporter {
235 | protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean) {
236 | as(msg, pos)
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/userinfo.ini.example:
--------------------------------------------------------------------------------
1 |
2 | user=
3 | passwd=
--------------------------------------------------------------------------------